feat: split daemon activation into bare bring-up and setPrimary

This commit is contained in:
cd-amn
2026-05-15 08:23:17 +00:00
parent c9c9f77e39
commit a03f13626b
4 changed files with 237 additions and 294 deletions

View File

@@ -17,7 +17,7 @@
constexpr const char* JSON_ALLOWEDIPADDRESSRANGES = "allowedIPAddressRanges";
constexpr int HANDSHAKE_POLL_MSEC = 250;
constexpr int STAGING_HANDSHAKE_TIMEOUT_MSEC = 30000;
constexpr int ACTIVATION_TIMEOUT_MSEC = 30000;
namespace {
@@ -35,19 +35,8 @@ Daemon::Daemon(QObject* parent) : QObject(parent) {
Q_ASSERT(s_daemon == nullptr);
s_daemon = this;
m_handshakeTimer.setSingleShot(true);
connect(&m_handshakeTimer, &QTimer::timeout, this, &Daemon::checkHandshake);
m_stagingHandshakeTimer.setSingleShot(false);
connect(&m_stagingHandshakeTimer, &QTimer::timeout,
this, &Daemon::checkStagingHandshake);
m_stagingTimeoutTimer.setSingleShot(true);
connect(&m_stagingTimeoutTimer, &QTimer::timeout, this, [this] {
logger.warning() << "Staging tunnel handshake timed out";
emit stagingFailed();
discardStaging();
});
m_activationTimer.setSingleShot(false);
connect(&m_activationTimer, &QTimer::timeout, this, &Daemon::checkActivations);
}
Daemon::~Daemon() {
@@ -68,27 +57,36 @@ Daemon* Daemon::instance() {
return s_daemon;
}
bool Daemon::activate(const InterfaceConfig& config) {
// Brings up the VPN interface for a fresh connection.
logger.debug() << "Activating interface" << config.m_ifname;
auto emit_failure_guard = qScopeGuard([this] { emit activationFailure(); });
bool Daemon::activate(const QString& ifname, const InterfaceConfig& config) {
logger.debug() << "Activating tunnel" << ifname;
prepareActivation(config);
WireguardUtils* wg = m_tunnels.value(config.m_ifname);
WireguardUtils* wg = m_tunnels.value(ifname);
if (!wg) {
wg = createWgUtils();
if (!wg) {
logger.error() << "Failed to create wireguard utils.";
return false;
}
m_tunnels.insert(config.m_ifname, wg);
m_tunnels.insert(ifname, wg);
}
m_primaryIfname = config.m_ifname;
if (m_primaryIfname.isEmpty()) {
m_primaryIfname = ifname;
}
ConnectionState& cs = m_connections[ifname];
cs.m_config = config;
cs.m_date = QDateTime();
cs.m_deadline = QDateTime::currentDateTime().addMSecs(ACTIVATION_TIMEOUT_MSEC);
const bool isPrimaryActivation = (m_primaryIfname == ifname);
auto failure_guard = qScopeGuard([this, isPrimaryActivation] {
if (isPrimaryActivation) emit activationFailure();
});
prepareActivation(config);
// Bring up the wireguard interface if not already done.
if (!wg->interfaceExists()) {
// Create the interface.
if (!wg->addInterface(config)) {
logger.error() << "Interface creation failed.";
return false;
@@ -105,9 +103,11 @@ bool Daemon::activate(const InterfaceConfig& config) {
}
}
// Configure routing for excluded addresses.
for (const QString& i : config.m_excludedAddresses) {
addExclusionRoute(IPAddress(i));
if (!config.m_serverIpv4AddrIn.isEmpty()) {
addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
}
if (!config.m_serverIpv6AddrIn.isEmpty()) {
addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
}
// Add the peer to this interface.
@@ -116,27 +116,92 @@ bool Daemon::activate(const InterfaceConfig& config) {
return false;
}
if (!maybeUpdateResolvers(config)) {
return false;
if (!m_activationTimer.isActive()) {
m_activationTimer.start(HANDSHAKE_POLL_MSEC);
}
failure_guard.dismiss();
return true;
}
bool Daemon::setPrimary(const QString& ifname, const InterfaceConfig& config) {
logger.debug() << "setPrimary" << ifname;
auto failure_guard = qScopeGuard([this] { emit activationFailure(); });
WireguardUtils* wg = m_tunnels.value(ifname);
if (!wg) {
logger.error() << "setPrimary: no tunnel for" << ifname;
return false;
}
m_primaryIfname = ifname;
for (const QString& i : config.m_excludedAddresses) {
addExclusionRoute(IPAddress(i));
}
// set routing
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
if (!wg->updateRoutePrefix(ip)) {
logger.debug() << "Routing configuration failed for" << ip.toString();
return false;
logger.warning() << "setPrimary: route setup failed for" << ip.toString();
}
}
bool status = run(Up, config);
logger.debug() << "Connection status:" << status;
if (status) {
m_connections[config.m_ifname] = ConnectionState(config);
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
emit_failure_guard.dismiss();
return true;
if (!maybeUpdateResolvers(config)) {
logger.warning() << "setPrimary: DNS resolver update failed";
}
return false;
if (!run(Up, config)) {
return false;
}
m_connections[ifname].m_config = config;
failure_guard.dismiss();
return true;
}
bool Daemon::deactivateTunnel(const QString& ifname) {
logger.debug() << "deactivateTunnel" << ifname;
WireguardUtils* wg = m_tunnels.value(ifname);
const ConnectionState cs = m_connections.value(ifname);
const InterfaceConfig& config = cs.m_config;
const bool wasPrimary = (ifname == m_primaryIfname);
if (wg) {
if (wasPrimary) {
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
wg->deleteRoutePrefix(ip);
}
}
wg->deletePeer(config);
auto removeExclusion = [&](const QString& addr) {
if (addr.isEmpty()) {
return;
}
IPAddress ip(addr);
if (m_excludedAddrSet.contains(ip)) {
delExclusionRoute(ip);
}
};
removeExclusion(config.m_serverIpv4AddrIn);
removeExclusion(config.m_serverIpv6AddrIn);
if (wasPrimary) {
for (const QString& i : config.m_excludedAddresses) {
removeExclusion(i);
}
}
wg->deleteInterface();
m_tunnels.remove(ifname);
delete wg;
}
m_connections.remove(ifname);
if (wasPrimary) {
m_primaryIfname.clear();
}
return true;
}
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
@@ -203,7 +268,8 @@ bool Daemon::delExclusionRoute(const IPAddress& prefix) {
return true;
}
m_excludedAddrSet.remove(prefix);
return primaryWgutils()->deleteExclusionRoute(prefix);
WireguardUtils* wg = primaryWgutils();
return wg && wg->deleteExclusionRoute(prefix);
}
// static
@@ -421,12 +487,10 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
}
bool Daemon::deactivate(bool emitSignals) {
WireguardUtils* wg = primaryWgutils();
const QString primary = m_primaryIfname;
// Deactivate the main interface.
if (!m_connections.isEmpty()) {
const ConnectionState& state = m_connections.first();
if (!run(Down, state.m_config)) {
if (m_connections.contains(primary)) {
if (!run(Down, m_connections.value(primary).m_config)) {
return false;
}
}
@@ -435,36 +499,22 @@ bool Daemon::deactivate(bool emitSignals) {
emit disconnected();
}
// Cleanup DNS
if (!dnsutils()->restoreResolvers()) {
logger.warning() << "Failed to restore DNS resolvers.";
}
if (wg) {
// Cleanup peers and routing
for (const ConnectionState& state : m_connections) {
const InterfaceConfig& config = state.m_config;
logger.debug() << "Deleting routes for" << config.m_ifname;
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
wg->deleteRoutePrefix(ip);
}
wg->deletePeer(config);
}
// Cleanup routing for excluded addresses.
for (auto iterator = m_excludedAddrSet.constBegin();
iterator != m_excludedAddrSet.constEnd(); ++iterator) {
wg->deleteExclusionRoute(iterator.key());
const QStringList ifnames = m_tunnels.keys();
for (const QString& ifname : ifnames) {
if (ifname != primary) {
deactivateTunnel(ifname);
}
}
m_excludedAddrSet.clear();
m_connections.clear();
// Delete the interface
if (!wg) {
return false;
if (m_tunnels.contains(primary)) {
deactivateTunnel(primary);
}
return wg->deleteInterface();
m_activationTimer.stop();
return true;
}
QString Daemon::logs() {
@@ -478,12 +528,12 @@ QJsonObject Daemon::getStatus() {
logger.debug() << "Status request";
WireguardUtils* wg = primaryWgutils();
if (!wg || !wg->interfaceExists() || m_connections.isEmpty()) {
if (!wg || !wg->interfaceExists() || !m_connections.contains(m_primaryIfname)) {
json.insert("connected", QJsonValue(false));
return json;
}
const ConnectionState& connection = m_connections.first();
const ConnectionState& connection = m_connections.value(m_primaryIfname);
QList<WireguardUtils::PeerStatus> peers = wg->getPeerStatus();
for (const WireguardUtils::PeerStatus& status : peers) {
if (status.m_pubkey != connection.m_config.m_serverPublicKey) {
@@ -504,188 +554,50 @@ QJsonObject Daemon::getStatus() {
return json;
}
void Daemon::checkHandshake() {
WireguardUtils* wg = primaryWgutils();
if (!wg) {
return;
}
void Daemon::checkActivations() {
const QDateTime now = QDateTime::currentDateTime();
QStringList timedOut;
bool anyPending = false;
logger.debug() << "Checking for handshake...";
for (auto it = m_connections.begin(); it != m_connections.end(); ++it) {
const QString& ifname = it.key();
ConnectionState& cs = it.value();
if (cs.m_date.isValid()) {
continue; // already handshaked
}
logger.debug() << "awaiting" << cs.m_config.m_serverPublicKey;
int pendingHandshakes = 0;
QList<WireguardUtils::PeerStatus> peers = wg->getPeerStatus();
for (ConnectionState& connection : m_connections) {
const InterfaceConfig& config = connection.m_config;
if (connection.m_date.isValid()) {
WireguardUtils* wg = m_tunnels.value(ifname);
bool handshaked = false;
if (wg) {
for (const WireguardUtils::PeerStatus& status : wg->getPeerStatus()) {
if (status.m_pubkey != cs.m_config.m_serverPublicKey) {
continue;
}
if (status.m_handshake != 0) {
cs.m_date.setMSecsSinceEpoch(status.m_handshake);
emit tunnelConnected(ifname, status.m_pubkey);
handshaked = true;
}
}
}
if (handshaked) {
continue;
}
logger.debug() << "awaiting" << config.m_serverPublicKey;
// Check if the handshake has completed.
for (const WireguardUtils::PeerStatus& status : peers) {
if (config.m_serverPublicKey != status.m_pubkey) {
continue;
}
if (status.m_handshake != 0) {
connection.m_date.setMSecsSinceEpoch(status.m_handshake);
emit connected(status.m_pubkey);
}
}
if (!connection.m_date.isValid()) {
pendingHandshakes++;
if (cs.m_deadline.isValid() && now > cs.m_deadline) {
timedOut.append(ifname);
} else {
anyPending = true;
}
}
// Check again if there were connections that haven't completed a handshake.
if (pendingHandshakes > 0) {
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
for (const QString& ifname : timedOut) {
logger.warning() << "Tunnel handshake timed out:" << ifname;
emit tunnelHandshakeFailed(ifname);
deactivateTunnel(ifname);
}
if (!anyPending) {
m_activationTimer.stop();
}
}
bool Daemon::activateStaging(const InterfaceConfig& config) {
logger.debug() << "activateStaging: bringing up staging tunnel on" << config.m_ifname;
discardStaging();
if (!config.m_serverIpv4AddrIn.isEmpty())
addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn));
if (!config.m_serverIpv6AddrIn.isEmpty())
addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn));
WireguardUtils* wg = createWgUtils();
if (!wg) {
logger.error() << "activateStaging: failed to create utils";
emit stagingFailed();
return false;
}
m_tunnels.insert(config.m_ifname, wg);
m_stagingConfig = config;
if (!wg->addInterface(config)) {
logger.error() << "activateStaging: addInterface failed";
emit stagingFailed();
discardStaging();
return false;
}
if (supportIPUtils()) {
if (!iputils()->addInterfaceIPs(config) || !iputils()->setMTUAndUp(config)) {
logger.error() << "activateStaging: IP setup failed";
emit stagingFailed();
discardStaging();
return false;
}
}
if (!wg->updatePeer(config)) {
logger.error() << "activateStaging: updatePeer failed";
emit stagingFailed();
discardStaging();
return false;
}
m_stagingHandshakeTimer.start(HANDSHAKE_POLL_MSEC);
m_stagingTimeoutTimer.start(STAGING_HANDSHAKE_TIMEOUT_MSEC);
return true;
}
bool Daemon::discardStaging() {
m_stagingHandshakeTimer.stop();
m_stagingTimeoutTimer.stop();
if (!m_stagingConfig.m_serverIpv4AddrIn.isEmpty()) {
IPAddress addr(m_stagingConfig.m_serverIpv4AddrIn);
if (m_excludedAddrSet.contains(addr)) delExclusionRoute(addr);
}
if (!m_stagingConfig.m_serverIpv6AddrIn.isEmpty()) {
IPAddress addr(m_stagingConfig.m_serverIpv6AddrIn);
if (m_excludedAddrSet.contains(addr)) delExclusionRoute(addr);
}
WireguardUtils* wg = m_tunnels.value(m_stagingConfig.m_ifname);
if (!wg) return true;
wg->deleteInterface();
delete wg;
m_tunnels.remove(m_stagingConfig.m_ifname);
m_stagingConfig = InterfaceConfig{};
return true;
}
void Daemon::checkStagingHandshake() {
WireguardUtils* wg = m_tunnels.value(m_stagingConfig.m_ifname);
if (!wg) {
m_stagingHandshakeTimer.stop();
return;
}
for (const WireguardUtils::PeerStatus& peer : wg->getPeerStatus()) {
if (peer.m_handshake != 0) {
logger.debug() << "Staging tunnel handshake confirmed for" << peer.m_pubkey;
m_stagingHandshakeTimer.stop();
m_stagingTimeoutTimer.stop();
emit stagingConnected(peer.m_pubkey);
return;
}
}
}
bool Daemon::promoteStagingToActive(const InterfaceConfig& newConfig) {
logger.debug() << "promoteStagingToActive";
const QString stagingIfname = m_stagingConfig.m_ifname;
WireguardUtils* staging = m_tunnels.value(stagingIfname);
if (!staging) {
logger.error() << "promoteStagingToActive: no staging utils";
return false;
}
m_stagingHandshakeTimer.stop();
m_stagingTimeoutTimer.stop();
const QString oldIfname = m_primaryIfname;
WireguardUtils* oldWg = m_tunnels.value(oldIfname);
if (!m_connections.isEmpty()) {
const InterfaceConfig& oldConfig = m_connections.first().m_config;
if (oldWg) {
for (const IPAddress& ip : oldConfig.m_allowedIPAddressRanges) {
oldWg->deleteRoutePrefix(ip);
}
oldWg->deletePeer(oldConfig);
}
if (!oldConfig.m_serverIpv4AddrIn.isEmpty()) {
IPAddress addr(oldConfig.m_serverIpv4AddrIn);
if (m_excludedAddrSet.contains(addr)) delExclusionRoute(addr);
}
if (!oldConfig.m_serverIpv6AddrIn.isEmpty()) {
IPAddress addr(oldConfig.m_serverIpv6AddrIn);
if (m_excludedAddrSet.contains(addr)) delExclusionRoute(addr);
}
}
if (oldWg) {
oldWg->deleteInterface();
delete oldWg;
m_tunnels.remove(oldIfname);
}
m_connections.clear();
m_primaryIfname = stagingIfname;
m_stagingConfig = InterfaceConfig{};
for (const IPAddress& ip : newConfig.m_allowedIPAddressRanges) {
if (!staging->updateRoutePrefix(ip)) {
logger.warning() << "promoteStagingToActive: route setup failed for" << ip.toString();
}
}
if (!maybeUpdateResolvers(newConfig)) {
logger.warning() << "promoteStagingToActive: DNS resolver update failed";
}
m_connections[newConfig.m_ifname] = ConnectionState(newConfig);
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
return true;
}

View File

@@ -32,10 +32,14 @@ class Daemon : public QObject {
static bool parseConfig(const QJsonObject& obj, InterfaceConfig& config);
virtual bool activate(const InterfaceConfig& config);
bool activate(const QString& ifname, const InterfaceConfig& config);
bool setPrimary(const QString& ifname, const InterfaceConfig& config);
bool deactivateTunnel(const QString& ifname);
virtual bool deactivate(bool emitSignals = true);
virtual QJsonObject getStatus();
const QString& primaryIfname() const { return m_primaryIfname; }
// Callback before any Activating measure is done
virtual void prepareActivation(const InterfaceConfig& config, int inetAdapterIndex = 0) {
Q_UNUSED(config) };
@@ -45,12 +49,9 @@ class Daemon : public QObject {
QString logs();
void cleanLogs();
bool activateStaging(const InterfaceConfig& config);
bool discardStaging();
bool promoteStagingToActive(const InterfaceConfig& newConfig);
signals:
void connected(const QString& pubkey);
void tunnelConnected(const QString& ifname, const QString& pubkey);
void tunnelHandshakeFailed(const QString& ifname);
/**
* Can be fired if a call to activate() was unsucessfull
* and connected systems should rollback
@@ -58,17 +59,13 @@ class Daemon : public QObject {
void activationFailure();
void disconnected();
void backendFailure(DaemonError reason = DaemonError::ERROR_FATAL);
void stagingConnected(const QString& pubkey);
void stagingFailed();
private:
bool maybeUpdateResolvers(const InterfaceConfig& config);
bool addExclusionRoute(const IPAddress& address);
bool delExclusionRoute(const IPAddress& address);
void checkStagingHandshake();
QTimer m_stagingHandshakeTimer;
QTimer m_stagingTimeoutTimer;
InterfaceConfig m_stagingConfig;
void checkActivations();
QTimer m_activationTimer;
protected:
virtual bool run(Op op, const InterfaceConfig& config) {
@@ -90,18 +87,16 @@ class Daemon : public QObject {
static bool parseStringList(const QJsonObject& obj, const QString& name,
QStringList& list);
void checkHandshake();
class ConnectionState {
public:
ConnectionState(){};
ConnectionState(const InterfaceConfig& config) { m_config = config; }
QDateTime m_date;
QDateTime m_deadline;
InterfaceConfig m_config;
};
QMap<QString, ConnectionState> m_connections;
QHash<IPAddress, int> m_excludedAddrSet;
QTimer m_handshakeTimer;
};
#endif // DAEMON_H

View File

@@ -31,16 +31,14 @@ DaemonLocalServerConnection::DaemonLocalServerConnection(QObject* parent,
&DaemonLocalServerConnection::readData);
Daemon* daemon = Daemon::instance();
connect(daemon, &Daemon::connected, this,
&DaemonLocalServerConnection::connected);
connect(daemon, &Daemon::tunnelConnected,
this, &DaemonLocalServerConnection::onTunnelConnected);
connect(daemon, &Daemon::tunnelHandshakeFailed,
this, &DaemonLocalServerConnection::onTunnelHandshakeFailed);
connect(daemon, &Daemon::disconnected, this,
&DaemonLocalServerConnection::disconnected);
connect(daemon, &Daemon::backendFailure, this,
&DaemonLocalServerConnection::backendFailure);
connect(daemon, &Daemon::stagingConnected,
this, &DaemonLocalServerConnection::stagingConnected);
connect(daemon, &Daemon::stagingFailed,
this, &DaemonLocalServerConnection::stagingFailed);
}
DaemonLocalServerConnection::~DaemonLocalServerConnection() {
@@ -111,18 +109,22 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
InterfaceConfig config;
if (!Daemon::parseConfig(obj, config)) {
logger.error() << "Invalid configuration";
emit disconnected();
disconnected();
return;
}
if (!Daemon::instance()->activate(config)) {
m_responseStyle[config.m_ifname] = ResponseStyle::LegacyActive;
if (!Daemon::instance()->activate(config.m_ifname, config) ||
!Daemon::instance()->setPrimary(config.m_ifname, config)) {
logger.error() << "Failed to activate the interface";
emit disconnected();
Daemon::instance()->deactivateTunnel(config.m_ifname);
m_responseStyle.remove(config.m_ifname);
disconnected();
}
return;
}
if (type == "deactivate") {
m_responseStyle.clear();
Daemon::instance()->deactivate(true);
return;
}
@@ -133,12 +135,30 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.error() << "activateStaging: invalid configuration";
return;
}
Daemon::instance()->activateStaging(config);
m_responseStyle[config.m_ifname] = ResponseStyle::LegacyStaging;
if (!Daemon::instance()->activate(config.m_ifname, config)) {
logger.error() << "activateStaging: failed";
Daemon::instance()->deactivateTunnel(config.m_ifname);
m_responseStyle.remove(config.m_ifname);
QJsonObject reply;
reply.insert("type", "stagingFailed");
write(reply);
}
return;
}
if (type == "discardStaging") {
Daemon::instance()->discardStaging();
QString stagingIfname;
for (auto it = m_responseStyle.constBegin(); it != m_responseStyle.constEnd(); ++it) {
if (it.value() == ResponseStyle::LegacyStaging) {
stagingIfname = it.key();
break;
}
}
if (!stagingIfname.isEmpty()) {
Daemon::instance()->deactivateTunnel(stagingIfname);
m_responseStyle.remove(stagingIfname);
}
return;
}
@@ -148,8 +168,15 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.error() << "promoteStagingToActive: invalid configuration";
return;
}
if (!Daemon::instance()->promoteStagingToActive(config)) {
// TODO: Order is broken here, new tunnel should be set primary before deactivating the old one.
const QString oldPrimary = Daemon::instance()->primaryIfname();
if (!oldPrimary.isEmpty() && oldPrimary != config.m_ifname) {
Daemon::instance()->deactivateTunnel(oldPrimary);
m_responseStyle.remove(oldPrimary);
}
if (!Daemon::instance()->setPrimary(config.m_ifname, config)) {
logger.error() << "promoteStagingToActive failed";
Daemon::instance()->deactivateTunnel(config.m_ifname);
}
return;
}
@@ -177,10 +204,27 @@ void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
logger.warning() << "Invalid command:" << type;
}
void DaemonLocalServerConnection::connected(const QString& pubkey) {
void DaemonLocalServerConnection::onTunnelConnected(const QString& ifname,
const QString& pubkey) {
const ResponseStyle style =
m_responseStyle.value(ifname, ResponseStyle::LegacyActive);
m_responseStyle.remove(ifname);
QJsonObject obj;
obj.insert("type", "connected");
obj.insert("pubkey", QJsonValue(pubkey));
obj.insert("type", style == ResponseStyle::LegacyStaging ? "stagingConnected"
: "connected");
obj.insert("pubkey", pubkey);
write(obj);
}
void DaemonLocalServerConnection::onTunnelHandshakeFailed(const QString& ifname) {
const ResponseStyle style =
m_responseStyle.value(ifname, ResponseStyle::LegacyActive);
m_responseStyle.remove(ifname);
QJsonObject obj;
obj.insert("type", style == ResponseStyle::LegacyStaging ? "stagingFailed"
: "disconnected");
write(obj);
}
@@ -197,19 +241,6 @@ void DaemonLocalServerConnection::backendFailure(DaemonError err) {
write(obj);
}
void DaemonLocalServerConnection::stagingConnected(const QString& pubkey) {
QJsonObject obj;
obj.insert("type", "stagingConnected");
obj.insert("pubkey", pubkey);
write(obj);
}
void DaemonLocalServerConnection::stagingFailed() {
QJsonObject obj;
obj.insert("type", "stagingFailed");
write(obj);
}
void DaemonLocalServerConnection::write(const QJsonObject& obj) {
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
m_socket->write("\n");

View File

@@ -5,7 +5,9 @@
#ifndef DAEMONLOCALSERVERCONNECTION_H
#define DAEMONLOCALSERVERCONNECTION_H
#include <QMap>
#include <QObject>
#include <QString>
#include "daemonerrors.h"
@@ -23,11 +25,12 @@ class DaemonLocalServerConnection final : public QObject {
void parseCommand(const QByteArray& json);
void connected(const QString& pubkey);
enum class ResponseStyle { LegacyActive, LegacyStaging };
void onTunnelConnected(const QString& ifname, const QString& pubkey);
void onTunnelHandshakeFailed(const QString& ifname);
void disconnected();
void backendFailure(DaemonError err);
void stagingConnected(const QString& pubkey);
void stagingFailed();
void write(const QJsonObject& obj);
@@ -35,6 +38,8 @@ class DaemonLocalServerConnection final : public QObject {
QLocalSocket* m_socket = nullptr;
QByteArray m_buffer;
QMap<QString, ResponseStyle> m_responseStyle;
};
#endif // DAEMONLOCALSERVERCONNECTION_H