diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index c3af531a..49504e3c 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -36,7 +36,6 @@ set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h - ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h ) if(NOT IOS) @@ -86,7 +85,6 @@ set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp - ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp ) if(NOT IOS) @@ -175,11 +173,13 @@ if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) ${CLIENT_ROOT_DIR}/protocols/wireguardprotocol.h ${CLIENT_ROOT_DIR}/protocols/xrayprotocol.h ${CLIENT_ROOT_DIR}/protocols/awgprotocol.h + ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.h ) set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/core/ipcclient.cpp ${CLIENT_ROOT_DIR}/core/privileged_process.cpp + ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp ${CLIENT_ROOT_DIR}/ui/systemtray_notificationhandler.cpp ${CLIENT_ROOT_DIR}/protocols/openvpnprotocol.cpp ${CLIENT_ROOT_DIR}/protocols/openvpnovercloakprotocol.cpp diff --git a/client/core/ipcclient.cpp b/client/core/ipcclient.cpp index 69edcd15..52c8441f 100644 --- a/client/core/ipcclient.cpp +++ b/client/core/ipcclient.cpp @@ -18,6 +18,12 @@ bool IpcClient::isSocketConnected() const return m_isSocketConnected; } +void IpcClient::close() +{ + if (m_localSocket) + m_localSocket->close(); +} + IpcClient *IpcClient::Instance() { return m_instance; diff --git a/client/core/ipcclient.h b/client/core/ipcclient.h index ad2e6b6e..a13fafcf 100644 --- a/client/core/ipcclient.h +++ b/client/core/ipcclient.h @@ -23,6 +23,7 @@ public: static QSharedPointer CreatePrivilegedProcess(); bool isSocketConnected() const; + void close(); signals: diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 1081bcae..dfdc03a3 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -14,10 +14,8 @@ #include #include -#include "ipaddress.h" #include "leakdetector.h" #include "logger.h" -#include "models/server.h" #include "daemon/daemonerrors.h" #include "protocols/protocols_defs.h" @@ -38,7 +36,7 @@ LocalSocketController::LocalSocketController() { m_socket = new QLocalSocket(this); connect(m_socket, &QLocalSocket::connected, this, &LocalSocketController::daemonConnected); - connect(m_socket, &QLocalSocket::disconnected, this, + connect(m_socket, &QLocalSocket::disconnected, this, [&] { errorOccurred(QLocalSocket::PeerClosedError); }); connect(m_socket, &QLocalSocket::errorOccurred, this, &LocalSocketController::errorOccurred); @@ -115,7 +113,6 @@ void LocalSocketController::daemonConnected() { } void LocalSocketController::activate(const QJsonObject &rawConfig) { - QString protocolName = rawConfig.value("protocol").toString(); int splitTunnelType = rawConfig.value("splitTunnelType").toInt(); @@ -131,13 +128,17 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { // 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)); + m_deviceIpv4 = wgConfig.value(amnezia::config_key::client_ip).toString(); // set up IPv6 unique-local-address, ULA, with "fd00::/8" prefix, not globally routable. - // this will be default IPv6 gateway, OS recognizes that IPv6 link is local and switches to IPv4. - // Otherwise some OSes (Linux) try IPv6 forever and hang. + // this will be default IPv6 gateway, OS recognizes that IPv6 link + // is local and switches to IPv4. + // Otherwise some OSes (Linux) try IPv6 forever and hang. // https://en.wikipedia.org/wiki/Unique_local_address (RFC 4193) // https://man7.org/linux/man-pages/man5/gai.conf.5.html - json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); // simply "dead::1" is globally-routable, don't use it + + // simply "dead::1" is globally-routable, don't use it + json.insert("deviceIpv6Address", "fd58:baa6:dead::1"); json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key)); json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key)); @@ -212,7 +213,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("allowedIPAddressRanges", jsAllowedIPAddesses); - QJsonArray jsExcludedAddresses; jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName)); if (splitTunnelType == 2) { @@ -406,6 +406,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) { } if (type == "status") { + QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway"); if (!serverIpv4Gateway.isString()) { logger.error() << "Unexpected serverIpv4Gateway value"; @@ -450,6 +451,11 @@ void LocalSocketController::parseCommand(const QByteArray& command) { logger.debug() << "Handshake completed with:" << pubkey.toString(); + + checkStatus(); + + emit statusUpdated("", m_deviceIpv4, 0, 0); + emit connected(pubkey.toString()); return; } diff --git a/client/mozilla/localsocketcontroller.h b/client/mozilla/localsocketcontroller.h index f0652e5a..4070c752 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -12,6 +12,7 @@ #include "controllerimpl.h" + class QJsonObject; class LocalSocketController final : public ControllerImpl { @@ -58,6 +59,7 @@ class LocalSocketController final : public ControllerImpl { QByteArray m_buffer; + QString m_deviceIpv4; std::function m_logCallback = nullptr; QTimer m_initializingTimer; diff --git a/client/mozilla/networkwatcher.cpp b/client/mozilla/networkwatcher.cpp index 59caf1f2..6828a867 100644 --- a/client/mozilla/networkwatcher.cpp +++ b/client/mozilla/networkwatcher.cpp @@ -11,7 +11,6 @@ #include "logger.h" //#include "mozillavpn.h" #include "networkwatcherimpl.h" -#include "platforms/dummy/dummynetworkwatcher.h" //#include "settingsholder.h" #ifdef MZ_WINDOWS @@ -51,7 +50,7 @@ NetworkWatcher::NetworkWatcher() { MZ_COUNT_CTOR(NetworkWatcher); } NetworkWatcher::~NetworkWatcher() { MZ_COUNT_DTOR(NetworkWatcher); } void NetworkWatcher::initialize() { - logger.debug() << "Initialize"; + logger.debug() << "Initialize NetworkWatcher"; #if defined(MZ_WINDOWS) m_impl = new WindowsNetworkWatcher(this); @@ -69,14 +68,17 @@ void NetworkWatcher::initialize() { m_impl = new DummyNetworkWatcher(this); #endif + connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this, &NetworkWatcher::unsecuredNetwork); connect(m_impl, &NetworkWatcherImpl::networkChanged, this, &NetworkWatcher::networkChange); - + connect(m_impl, &NetworkWatcherImpl::sleepMode, this, + &NetworkWatcher::onSleepMode); m_impl->initialize(); + // TODO: IMPL FOR AMNEZIA #if 0 SettingsHolder* settingsHolder = SettingsHolder::instance(); @@ -117,11 +119,16 @@ void NetworkWatcher::settingsChanged() { #endif } +void NetworkWatcher::onSleepMode() +{ + logger.debug() << "Resumed from sleep mode"; + emit sleepMode(); +} + void NetworkWatcher::unsecuredNetwork(const QString& networkName, const QString& networkId) { logger.debug() << "Unsecured network:" << logger.sensitive(networkName) << "id:" << logger.sensitive(networkId); - #ifndef UNIT_TEST if (!m_reportUnsecuredNetwork) { logger.debug() << "Disabled. Ignoring unsecured network"; diff --git a/client/mozilla/networkwatcher.h b/client/mozilla/networkwatcher.h index 43536dc3..f28e74fb 100644 --- a/client/mozilla/networkwatcher.h +++ b/client/mozilla/networkwatcher.h @@ -29,10 +29,13 @@ public: // false to restore. void simulateDisconnection(bool simulatedDisconnection); + void onSleepMode(); + QNetworkInformation::Reachability getReachability(); signals: void networkChange(); + void sleepMode(); private: void settingsChanged(); diff --git a/client/mozilla/networkwatcherimpl.h b/client/mozilla/networkwatcherimpl.h index 78117dca..54bd8e87 100644 --- a/client/mozilla/networkwatcherimpl.h +++ b/client/mozilla/networkwatcherimpl.h @@ -41,6 +41,8 @@ signals: // TODO: Only windows-networkwatcher has this, the other plattforms should // too. void networkChanged(QString newBSSID); + void sleepMode(); + private: bool m_active = false; diff --git a/client/mozilla/pinghelper.cpp b/client/mozilla/pinghelper.cpp index 44920bf6..d0aa4250 100644 --- a/client/mozilla/pinghelper.cpp +++ b/client/mozilla/pinghelper.cpp @@ -41,6 +41,7 @@ void PingHelper::start(const QString& 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 @@ -53,8 +54,10 @@ void PingHelper::start(const QString& serverIpv4Gateway, connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived, Qt::QueuedConnection); - connect(m_pingSender, &PingSender::criticalPingError, this, - []() { logger.info() << "Encountered Unrecoverable ping error"; }); + connect(m_pingSender, &PingSender::criticalPingError, this, [this]() { + logger.info() << "Encountered Unrecoverable ping error"; + emit connectionLose(); + }); // Reset the ping statistics m_sequence = 0; diff --git a/client/mozilla/pinghelper.h b/client/mozilla/pinghelper.h index 00466f53..f4c511b5 100644 --- a/client/mozilla/pinghelper.h +++ b/client/mozilla/pinghelper.h @@ -33,6 +33,8 @@ class PingHelper final : public QObject { signals: void pingSentAndReceived(qint64 msec); + void connectionLose(); + private: void nextPing(); diff --git a/client/mozilla/pingsenderfactory.cpp b/client/mozilla/pingsenderfactory.cpp index c7ecdec8..e4cbef91 100644 --- a/client/mozilla/pingsenderfactory.cpp +++ b/client/mozilla/pingsenderfactory.cpp @@ -5,27 +5,26 @@ #include "pingsenderfactory.h" #if defined(MZ_LINUX) || defined(MZ_ANDROID) -//# include "platforms/linux/linuxpingsender.h" + # include "platforms/linux/linuxpingsender.h" #elif defined(MZ_MACOS) || defined(MZ_IOS) -# include "platforms/macos/macospingsender.h" + # 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" + # include "platforms/windows/windowspingsender.h" +#elif defined(MZ_WASM) || defined(UNIT_TEST) + # include "platforms/dummy/dummypingsender.h" #else -# error "Unsupported platform" + # 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); + return new LinuxPingSender(source, parent); #elif defined(MZ_MACOS) || defined(MZ_IOS) - return new MacOSPingSender(source, parent); + return new MacOSPingSender(source, parent); #elif defined(MZ_WINDOWS) - return new WindowsPingSender(source, parent); + return new WindowsPingSender(source, parent); #else - return new DummyPingSender(source, parent); + return new DummyPingSender(source, parent); #endif } diff --git a/client/mozilla/pingsenderfactory.h b/client/mozilla/pingsenderfactory.h index 7482d36d..69e9c911 100644 --- a/client/mozilla/pingsenderfactory.h +++ b/client/mozilla/pingsenderfactory.h @@ -10,9 +10,10 @@ class QHostAddress; class QObject; class PingSenderFactory final { - public: - PingSenderFactory() = delete; - static PingSender* create(const QHostAddress& source, QObject* parent); +public: + PingSenderFactory() = delete; + static PingSender* create(const QHostAddress& source, QObject* parent); }; + #endif // PINGSENDERFACTORY_H diff --git a/client/platforms/linux/linuxnetworkwatcher.cpp b/client/platforms/linux/linuxnetworkwatcher.cpp index c8ae0fea..22eba3c7 100644 --- a/client/platforms/linux/linuxnetworkwatcher.cpp +++ b/client/platforms/linux/linuxnetworkwatcher.cpp @@ -41,6 +41,9 @@ void LinuxNetworkWatcher::initialize() { connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this, &LinuxNetworkWatcher::unsecuredNetwork); + connect(m_worker, &LinuxNetworkWatcherWorker::sleepMode, this, + &NetworkWatcherImpl::sleepMode); + // Let's wait a few seconds to allow the UI to be fully loaded and shown. // This is not strictly needed, but it's better for user experience because // it makes the UI faster to appear, plus it gives a bit of delay between the diff --git a/client/platforms/linux/linuxnetworkwatcherworker.cpp b/client/platforms/linux/linuxnetworkwatcherworker.cpp index 19ed3251..247a1d67 100644 --- a/client/platforms/linux/linuxnetworkwatcherworker.cpp +++ b/client/platforms/linux/linuxnetworkwatcherworker.cpp @@ -33,7 +33,21 @@ #define NM_802_11_AP_SEC_WEAK_CRYPTO \ (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104) + +enum NMState { + NM_STATE_UNKNOWN = 0, + NM_STATE_ASLEEP = 10, + NM_STATE_DISCONNECTED = 20, + NM_STATE_DISCONNECTING = 30, + NM_STATE_CONNECTING = 40, + NM_STATE_CONNECTED_LOCAL = 50, + NM_STATE_CONNECTED_SITE = 60, + NM_STATE_CONNECTED_GLOBAL = 70 +}; + + constexpr const char* DBUS_NETWORKMANAGER = "org.freedesktop.NetworkManager"; +constexpr const char* DBUS_NETWORKMANAGER_PATH = "/org/freedesktop/NetworkManager"; namespace { Logger logger("LinuxNetworkWatcherWorker"); @@ -73,7 +87,7 @@ void LinuxNetworkWatcherWorker::initialize() { // documentation: // https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html - QDBusInterface nm(DBUS_NETWORKMANAGER, "/org/freedesktop/NetworkManager", + QDBusInterface nm(DBUS_NETWORKMANAGER, DBUS_NETWORKMANAGER_PATH, DBUS_NETWORKMANAGER, QDBusConnection::systemBus()); if (!nm.isValid()) { logger.error() @@ -108,6 +122,12 @@ void LinuxNetworkWatcherWorker::initialize() { SLOT(propertyChanged(QString, QVariantMap, QStringList))); } + QDBusConnection::systemBus().connect(DBUS_NETWORKMANAGER, + DBUS_NETWORKMANAGER_PATH, + DBUS_NETWORKMANAGER, + "StateChanged", + this, SLOT(NMStateChanged(quint32))); + if (m_devicePaths.isEmpty()) { logger.warning() << "No wifi devices found"; return; @@ -173,5 +193,16 @@ void LinuxNetworkWatcherWorker::checkDevices() { emit unsecuredNetwork(ssid, bssid); break; } + } } + +void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state) +{ + if (state == NM_STATE_ASLEEP) { + emit sleepMode(); + } + + logger.debug() << "NMStateChanged " << state; +} + diff --git a/client/platforms/linux/linuxnetworkwatcherworker.h b/client/platforms/linux/linuxnetworkwatcherworker.h index cc4c6a36..9579fcef 100644 --- a/client/platforms/linux/linuxnetworkwatcherworker.h +++ b/client/platforms/linux/linuxnetworkwatcherworker.h @@ -23,6 +23,7 @@ class LinuxNetworkWatcherWorker final : public QObject { signals: void unsecuredNetwork(const QString& networkName, const QString& networkId); + void sleepMode(); public slots: void initialize(); @@ -30,6 +31,7 @@ class LinuxNetworkWatcherWorker final : public QObject { private slots: void propertyChanged(QString interface, QVariantMap properties, QStringList list); + void NMStateChanged(quint32 state); private: // We collect the list of DBus wifi network device paths during the diff --git a/client/platforms/linux/linuxpingsender.cpp b/client/platforms/linux/linuxpingsender.cpp new file mode 100644 index 00000000..16fd8d14 --- /dev/null +++ b/client/platforms/linux/linuxpingsender.cpp @@ -0,0 +1,185 @@ +/* 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 "linuxpingsender.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "leakdetector.h" +#include "logger.h" +#include "qhostaddress.h" + +namespace { +Logger logger("LinuxPingSender"); +} + +int LinuxPingSender::createSocket() { + // Try creating an ICMP socket. This would be the ideal choice, but it can + // fail depending on the kernel config (see: sys.net.ipv4.ping_group_range) + m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (m_socket >= 0) { + m_ident = 0; + return m_socket; + } + if ((errno != EPERM) && (errno != EACCES)) { + return -1; + } + + // As a fallback, create a raw socket, which requires root permissions + // or CAP_NET_RAW to be granted to the VPN client. + m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (m_socket < 0) { + return -1; + } + m_ident = getpid() & 0xffff; + + // Attach a BPF filter to discard everything but replies to our echo. + struct sock_filter bpf_prog[] = { + BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, m_ident, 1, 0), /* Ours? */ + BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected identifier. Reject. */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected type. Reject. */ + BPF_STMT(BPF_RET | BPF_K, ~0U), /* Packet passes the filter. */ + }; + struct sock_fprog filter = { + .len = sizeof(bpf_prog) / sizeof(struct sock_filter), + .filter = bpf_prog, + }; + setsockopt(m_socket, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); + + return m_socket; +} + +LinuxPingSender::LinuxPingSender(const QHostAddress& source, QObject* parent) + : PingSender(parent) { + MZ_COUNT_CTOR(LinuxPingSender); + + logger.debug() << "LinuxPingSender(" + logger.sensitive(source.toString()) + + ") created"; + + m_socket = createSocket(); + if (m_socket < 0) { + logger.error() << "Socket creation error: " << strerror(errno); + return; + } + + quint32 ipv4addr = INADDR_ANY; + if (!source.isNull()) { + ipv4addr = source.toIPv4Address(); + } + struct sockaddr_in addr; + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = qToBigEndian(ipv4addr); + + if (bind(m_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) { + close(m_socket); + m_socket = -1; + logger.error() << "bind error:" << strerror(errno); + return; + } + + m_notifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, this); + if (m_ident) { + connect(m_notifier, &QSocketNotifier::activated, this, + &LinuxPingSender::rawSocketReady); + } else { + connect(m_notifier, &QSocketNotifier::activated, this, + &LinuxPingSender::icmpSocketReady); + } +} + +LinuxPingSender::~LinuxPingSender() { + MZ_COUNT_DTOR(LinuxPingSender); + if (m_socket >= 0) { + close(m_socket); + } +} + +void LinuxPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { + quint32 ipv4dest = dest.toIPv4Address(); + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = qToBigEndian(ipv4dest); + + struct icmphdr packet; + memset(&packet, 0, sizeof(packet)); + packet.type = ICMP_ECHO; + packet.un.echo.id = htons(m_ident); + packet.un.echo.sequence = htons(sequence); + packet.checksum = inetChecksum(&packet, sizeof(packet)); + + int rc = sendto(m_socket, &packet, sizeof(packet), 0, (struct sockaddr*)&addr, + sizeof(addr)); + if (rc < 0) { + logger.error() << "failed to send:" << strerror(errno); + if (errno == ENETUNREACH) { + emit criticalPingError(); + } + } +} + +void LinuxPingSender::icmpSocketReady() { + socklen_t slen = 0; + unsigned char data[2048]; + int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen); + if (rc <= 0) { + logger.error() << "recvfrom failed:" << strerror(errno); + return; + } + + struct icmphdr packet; + if (rc >= (int)sizeof(packet)) { + memcpy(&packet, data, sizeof(packet)); + if (packet.type == ICMP_ECHOREPLY) { + emit recvPing(htons(packet.un.echo.sequence)); + } + } +} + +void LinuxPingSender::rawSocketReady() { + socklen_t slen = 0; + unsigned char data[2048]; + int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen); + if (rc <= 0) { + logger.error() << "recvfrom failed:" << strerror(errno); + return; + } + + // Check the IP header + const struct iphdr* ip = (struct iphdr*)data; + int iphdrlen = ip->ihl * 4; + if (rc < iphdrlen || iphdrlen < (int)sizeof(struct iphdr)) { + logger.error() << "malformed IP packet:" << strerror(errno); + return; + } + + // Check the ICMP packet + struct icmphdr packet; + if (inetChecksum(data + iphdrlen, rc - iphdrlen) != 0) { + logger.warning() << "invalid checksum"; + return; + } + if (rc >= (iphdrlen + (int)sizeof(packet))) { + memcpy(&packet, data + iphdrlen, sizeof(packet)); + quint16 id = htons(m_ident); + if ((packet.type == ICMP_ECHOREPLY) && (packet.un.echo.id == id)) { + emit recvPing(htons(packet.un.echo.sequence)); + } + } +} diff --git a/client/platforms/linux/linuxpingsender.h b/client/platforms/linux/linuxpingsender.h new file mode 100644 index 00000000..7bfb1ffa --- /dev/null +++ b/client/platforms/linux/linuxpingsender.h @@ -0,0 +1,39 @@ +/* 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 LINUXPINGSENDER_H +#define LINUXPINGSENDER_H + +#include + +#include "../client/mozilla/pingsender.h" + +class QSocketNotifier; + +class LinuxPingSender final : public PingSender { + Q_OBJECT + Q_DISABLE_COPY_MOVE(LinuxPingSender) + + public: + LinuxPingSender(const QHostAddress& source, QObject* parent = nullptr); + ~LinuxPingSender(); + + bool isValid() override { return (m_socket >= 0); }; + + void sendPing(const QHostAddress& dest, quint16 sequence) override; + + private: + int createSocket(); + + private slots: + void rawSocketReady(); + void icmpSocketReady(); + + private: + QSocketNotifier* m_notifier = nullptr; + int m_socket = -1; + quint16 m_ident = 0; +}; + +#endif // LINUXPINGSENDER_H diff --git a/client/platforms/macos/macosnetworkwatcher.h b/client/platforms/macos/macosnetworkwatcher.h index 2ae4cfd7..faddc565 100644 --- a/client/platforms/macos/macosnetworkwatcher.h +++ b/client/platforms/macos/macosnetworkwatcher.h @@ -10,8 +10,28 @@ #include "../ios/iosnetworkwatcher.h" #include "networkwatcherimpl.h" +#include +#include + + class QString; +// Inspired by https://ladydebug.com/blog/2020/05/21/programmatically-capture-energy-saver-event-on-mac/ +class PowerNotificationsListener +{ +public: + void registerForNotifications(); + +private: + static void sleepWakeupCallBack(void *refParam, io_service_t service, natural_t messageType, void *messageArgument); + +private: + IONotificationPortRef notifyPortRef = nullptr; // notification port allocated by IORegisterForSystemPower + io_object_t notifierObj = IO_OBJECT_NULL; // notifier object, used to deregister later + io_connect_t rootPowerDomain = IO_OBJECT_NULL; // a reference to the Root Power Domain IOService +}; + + class MacOSNetworkWatcher final : public IOSNetworkWatcher { public: MacOSNetworkWatcher(QObject* parent); @@ -25,6 +45,7 @@ class MacOSNetworkWatcher final : public IOSNetworkWatcher { private: void* m_delegate = nullptr; + PowerNotificationsListener m_powerlistener; }; #endif // MACOSNETWORKWATCHER_H diff --git a/client/platforms/macos/macosnetworkwatcher.mm b/client/platforms/macos/macosnetworkwatcher.mm index 3b4158a4..d4431941 100644 --- a/client/platforms/macos/macosnetworkwatcher.mm +++ b/client/platforms/macos/macosnetworkwatcher.mm @@ -38,6 +38,93 @@ Logger logger("MacOSNetworkWatcher"); @end +void PowerNotificationsListener::registerForNotifications() +{ + rootPowerDomain = IORegisterForSystemPower(this, ¬ifyPortRef, sleepWakeupCallBack, ¬ifierObj); + if (rootPowerDomain == IO_OBJECT_NULL) { + logger.debug() << "Failed to register for system power notifications!"; + return; + } + + logger.debug() << "IORegisterForSystemPower OK! Root port:" << rootPowerDomain; + + // add the notification port to the application runloop + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes); +} + +static void PowerNotificationsListener::sleepWakeupCallBack(void *refParam, io_service_t service, natural_t messageType, void *messageArgument) +{ + Q_UNUSED(service) + + auto listener = static_cast(refParam); + + switch (messageType) { + case kIOMessageCanSystemSleep: + /* Idle sleep is about to kick in. This message will not be sent for forced sleep. + * Applications have a chance to prevent sleep by calling IOCancelPowerChange. + * Most applications should not prevent idle sleep. Power Management waits up to + * 30 seconds for you to either allow or deny idle sleep. If you don’t acknowledge + * this power change by calling either IOAllowPowerChange or IOCancelPowerChange, + * the system will wait 30 seconds then go to sleep. + */ + + logger.debug() << "System power message: can system sleep?"; + + // Uncomment to cancel idle sleep + // IOCancelPowerChange(thiz->rootPowerDomain, reinterpret_cast(messageArgument)); + + // Allow idle sleep + IOAllowPowerChange(listener->rootPowerDomain, reinterpret_cast(messageArgument)); + break; + + case kIOMessageSystemWillNotSleep: + /* Announces that the system has retracted a previous attempt to sleep; it + * follows `kIOMessageCanSystemSleep`. + */ + logger.debug() << "System power message: system will NOT sleep."; + break; + + case kIOMessageSystemWillSleep: + /* The system WILL go to sleep. If you do not call IOAllowPowerChange or + * IOCancelPowerChange to acknowledge this message, sleep will be delayed by + * 30 seconds. + * + * NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess, + * however the system WILL still go to sleep. + */ + + logger.debug() << "System power message: system WILL sleep."; + + IOAllowPowerChange(listener->rootPowerDomain, reinterpret_cast(messageArgument)); + break; + + case kIOMessageSystemWillPowerOn: + /* Announces that the system is beginning to power the device tree; most devices + * are still unavailable at this point. + */ + /* From the documentation: + * + * - kIOMessageSystemWillPowerOn is delivered at early wakeup time, before most hardware + * has been powered on. Be aware that any attempts to access disk, network, the display, + * etc. may result in errors or blocking your process until those resources become + * available. + * + * So we do NOT log this event. + */ + break; + + case kIOMessageSystemHasPoweredOn: + /* Announces that the system and its devices have woken up. */ + logger.debug() << "System power message: system has powered on."; + break; + + default: + logger.debug() << "System power message: other event: " << messageType; + /* Not a system sleep and wake notification. */ + break; + } +} + MacOSNetworkWatcher::MacOSNetworkWatcher(QObject* parent) : IOSNetworkWatcher(parent) { MZ_COUNT_CTOR(MacOSNetworkWatcher); } @@ -66,6 +153,8 @@ void MacOSNetworkWatcher::start() { logger.debug() << "Delegate already registered"; return; } + + m_powerlistener.registerForNotifications(); CWWiFiClient* client = CWWiFiClient.sharedWiFiClient; if (!client) { diff --git a/client/platforms/windows/daemon/windowsdaemon.cpp b/client/platforms/windows/daemon/windowsdaemon.cpp index 620e0a1c..8668b4db 100644 --- a/client/platforms/windows/daemon/windowsdaemon.cpp +++ b/client/platforms/windows/daemon/windowsdaemon.cpp @@ -22,7 +22,6 @@ #include "logger.h" #include "platforms/windows/daemon/windowsfirewall.h" #include "platforms/windows/daemon/windowssplittunnel.h" -#include "platforms/windows/windowscommons.h" #include "windowsfirewall.h" #include "core/networkUtilities.h" diff --git a/client/platforms/windows/windowsnetworkwatcher.cpp b/client/platforms/windows/windowsnetworkwatcher.cpp index 7b1096b0..f72baa6e 100644 --- a/client/platforms/windows/windowsnetworkwatcher.cpp +++ b/client/platforms/windows/windowsnetworkwatcher.cpp @@ -32,9 +32,28 @@ WindowsNetworkWatcher::~WindowsNetworkWatcher() { } } +LRESULT WindowsNetworkWatcher::PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + auto obj = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + if (!obj){ + logger.debug() << "obj not casted"; + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + switch (uMsg) { + case WM_POWERBROADCAST: + if (wParam == PBT_APMRESUMESUSPEND) { + emit obj->sleepMode(); + } + break; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + return 0; +} + void WindowsNetworkWatcher::initialize() { logger.debug() << "initialize"; + DWORD negotiatedVersion; if (WlanOpenHandle(2, nullptr, &negotiatedVersion, &m_wlanHandle) != ERROR_SUCCESS) { @@ -51,6 +70,25 @@ void WindowsNetworkWatcher::initialize() { return; } + const wchar_t* className = L"PowerMonitorClass"; + WNDCLASS wc = { 0 }; + wc.lpfnWndProc = &WindowsNetworkWatcher::PowerWndProcCallback; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = className; + wc.cbWndExtra = sizeof(WindowsNetworkWatcher*); + + if (!RegisterClass(&wc)) { + logger.debug() << "Failed to register window class in createPowerMonitorWindow."; + return; + } + + HWND hwnd = CreateWindowEx(0, className, L"Power Monitor", 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), static_cast(this)); + if (!hwnd) { + logger.debug() << "Failed to create window in createPowerMonitorWindow."; + return; + } + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(this)); + logger.debug() << "callback registered"; } @@ -137,4 +175,4 @@ void WindowsNetworkWatcher::processWlan(PWLAN_NOTIFICATION_DATA data) { logger.debug() << "Unsecure network:" << logger.sensitive(ssid) << "id:" << logger.sensitive(bssid); emit unsecuredNetwork(ssid, bssid); -} \ No newline at end of file +} diff --git a/client/platforms/windows/windowsnetworkwatcher.h b/client/platforms/windows/windowsnetworkwatcher.h index 1b153746..0842ee7a 100644 --- a/client/platforms/windows/windowsnetworkwatcher.h +++ b/client/platforms/windows/windowsnetworkwatcher.h @@ -19,6 +19,7 @@ class WindowsNetworkWatcher final : public NetworkWatcherImpl { private: static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context); + static LRESULT PowerWndProcCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void processWlan(PWLAN_NOTIFICATION_DATA data); diff --git a/client/platforms/windows/windowspingsender.cpp b/client/platforms/windows/windowspingsender.cpp index 8b07a80f..a39ac2de 100644 --- a/client/platforms/windows/windowspingsender.cpp +++ b/client/platforms/windows/windowspingsender.cpp @@ -179,6 +179,7 @@ void WindowsPingSender::pingEventReady() { return; } QString errmsg = WindowsUtils::getErrorMessage(); + emit criticalPingError(); logger.error() << "No ping reply. Code: " << error << " Message: " << errmsg; return; diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index 056089b8..29ca247e 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -103,6 +103,11 @@ QString VpnProtocol::vpnGateway() const return m_vpnGateway; } +QString VpnProtocol::vpnLocalAddress() const +{ + return m_vpnLocalAddress; +} + VpnProtocol *VpnProtocol::factory(DockerContainer container, const QJsonObject &configuration) { switch (container) { diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h index bb71a5de..e482d1f8 100644 --- a/client/protocols/vpnprotocol.h +++ b/client/protocols/vpnprotocol.h @@ -63,6 +63,7 @@ public: QString routeGateway() const; QString vpnGateway() const; + QString vpnLocalAddress() const; static VpnProtocol* factory(amnezia::DockerContainer container, const QJsonObject &configuration); diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp index 80579f16..e3d5c9cc 100644 --- a/client/protocols/wireguardprotocol.cpp +++ b/client/protocols/wireguardprotocol.cpp @@ -17,6 +17,13 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject * [this](const QString &pubkey, const QDateTime &connectionTimestamp) { emit connectionStateChanged(Vpn::ConnectionState::Connected); }); + connect(m_impl.get(), &ControllerImpl::statusUpdated, this, + [this](const QString& serverIpv4Gateway, + const QString& deviceIpv4Address, uint64_t txBytes, + uint64_t rxBytes) { + m_vpnLocalAddress = deviceIpv4Address; + }); + connect(m_impl.get(), &ControllerImpl::disconnected, this, [this]() { emit connectionStateChanged(Vpn::ConnectionState::Disconnected); }); m_impl->initialize(nullptr, nullptr); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 042c51c7..24f2a699 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -6,7 +6,6 @@ #include #include -#include "core/controllers/serverController.h" #include #include #include @@ -14,7 +13,6 @@ #ifdef AMNEZIA_DESKTOP #include "core/ipcclient.h" - #include "ipc.h" #include #endif @@ -54,7 +52,6 @@ void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) { - #ifdef AMNEZIA_DESKTOP auto container = m_settings->defaultContainer(m_settings->defaultServerIndex()); @@ -86,6 +83,10 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } } + if (container != DockerContainer::Ipsec) { + IpcClient::Interface()->startNetworkCheck(m_vpnProtocol->vpnLocalAddress(), m_vpnProtocol->vpnLocalAddress()); + } + } else if (state == Vpn::ConnectionState::Error) { IpcClient::Interface()->flushDns(); @@ -97,6 +98,8 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } else if (state == Vpn::ConnectionState::Connecting) { } else if (state == Vpn::ConnectionState::Disconnected) { + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); } } #endif @@ -219,6 +222,11 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede .arg(serverIndex) .arg(ContainerProps::containerToString(container)); #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + if (m_IpcClient) { + m_IpcClient->close(); + m_IpcClient = nullptr; + } + if (!m_IpcClient) { m_IpcClient = new IpcClient(this); } @@ -237,6 +245,9 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede emit connectionStateChanged(Vpn::ConnectionState::Connecting); m_vpnConfiguration = vpnConfiguration; + m_serverIndex = serverIndex; + m_serverCredentials = credentials; + m_dockerContainer = container; #ifdef AMNEZIA_DESKTOP if (m_vpnProtocol) { @@ -275,12 +286,36 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede emit connectionStateChanged(Vpn::ConnectionState::Error); } +void VpnConnection::restartConnection() +{ + this->disconnectFromVpn(); +#ifdef Q_OS_LINUX + QThread::msleep(5000); +#endif + this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); +} + void VpnConnection::createProtocolConnections() { connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(Vpn::ConnectionState)), this, SLOT(onConnectionStateChanged(Vpn::ConnectionState))); connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); + +#ifdef AMNEZIA_DESKTOP + connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose, + this, [this]() { + qDebug() << "Connection Lose"; + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); + this->restartConnection(); + }); + connect(IpcClient::Interface().data(), &IpcInterfaceReplica::networkChange, + this, [this]() { + qDebug() << "Network change"; + this->restartConnection(); + }); +#endif } void VpnConnection::appendKillSwitchConfig() diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 0160edce..3d65e528 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -51,6 +51,7 @@ public slots: const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration); void disconnectFromVpn(); + void restartConnection(); void addRoutes(const QStringList &ips); @@ -77,6 +78,10 @@ private: QJsonObject m_routeMode; QString m_remoteAddress; + ServerCredentials m_serverCredentials; + int m_serverIndex; + DockerContainer m_dockerContainer; + // Only for iOS for now, check counters QTimer m_checkTimer; diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c0f031fe..1f4bcde9 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -32,5 +32,10 @@ class IpcInterface SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool updateResolvers(const QString& ifname, const QList& resolvers) ); + SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) ); + SLOT( bool stopNetworkCheck() ); + + SIGNAL( connectionLose() ); + SIGNAL( networkChange() ); }; diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 17f34499..bd51a686 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -25,8 +25,8 @@ #endif IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) - { + connect(&m_pingHelper, &PingHelper::connectionLose, this, &IpcServer::connectionLose); } int IpcServer::createPrivilegedProcess() @@ -188,6 +188,19 @@ void IpcServer::setLogsEnabled(bool enabled) } } +bool IpcServer::startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) +{ + m_pingHelper.start(serverIpv4Gateway, deviceIpv4Address); + return true; +} + +bool IpcServer::stopNetworkCheck() +{ + qDebug() << "stopNetworkCheck"; + m_pingHelper.stop(); + return true; +} + bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) { #ifdef Q_OS_WIN diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 9810046b..b4129f2d 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -6,6 +6,7 @@ #include #include #include "../client/daemon/interfaceconfig.h" +#include "../client/mozilla/pinghelper.h" #include "ipc.h" #include "ipcserverprocess.h" @@ -38,6 +39,8 @@ public: virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool disableKillSwitch() override; virtual bool updateResolvers(const QString& ifname, const QList& resolvers) override; + virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override; + virtual bool stopNetworkCheck() override; private: int m_localpid = 0; @@ -57,6 +60,8 @@ private: }; QMap m_processes; + PingHelper m_pingHelper; + }; #endif // IPCSERVER_H diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 28174774..3dc18dd4 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -56,7 +56,6 @@ set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/controllerimpl.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.h - ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/localsocketcontroller.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcherimpl.h ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.h @@ -80,7 +79,6 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/shared/leakdetector.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/dnspingsender.cpp - ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/localsocketcontroller.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/networkwatcher.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pinghelper.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/mozilla/pingsender.cpp @@ -212,6 +210,7 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h @@ -226,6 +225,7 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxpingsender.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 8a5079cb..56a817d0 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -5,9 +5,7 @@ #include "ipc.h" #include "localserver.h" -#include "utilities.h" -#include "router.h" #include "logger.h" #ifdef Q_OS_WIN @@ -47,6 +45,9 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), return; } + m_networkWatcher.initialize(); + connect(&m_networkWatcher, &NetworkWatcher::sleepMode, &m_ipcServer, &IpcServer::networkChange); + #ifdef Q_OS_LINUX // Signal handling for a proper shutdown. QObject::connect(qApp, &QCoreApplication::aboutToQuit, diff --git a/service/server/localserver.h b/service/server/localserver.h index 3c565d3b..47a7640d 100644 --- a/service/server/localserver.h +++ b/service/server/localserver.h @@ -11,7 +11,7 @@ #include "ipcserver.h" #include "../../client/daemon/daemonlocalserver.h" - +#include "../../client/mozilla/networkwatcher.h" #ifdef Q_OS_WIN #include "windows/daemon/windowsdaemon.h" @@ -41,6 +41,8 @@ public: IpcProcessTun2Socks m_tun2socks; QRemoteObjectHost m_serverNode; bool m_isRemotingEnabled = false; + + NetworkWatcher m_networkWatcher; #ifdef Q_OS_LINUX DaemonLocalServer server{qApp}; LinuxDaemon daemon;