From e792117be1dcccc2b43f964300560c05648b8319 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 12 Mar 2025 23:32:00 +0200 Subject: [PATCH 01/14] Add network status check for AWG/WG protocol --- client/cmake/sources.cmake | 30 +++ client/mozilla/localsocketcontroller.cpp | 19 ++ client/mozilla/localsocketcontroller.h | 7 + client/mozilla/pinghelper.cpp | 7 +- client/mozilla/pinghelper.h | 2 + client/mozilla/pingsenderfactory.cpp | 21 +- client/mozilla/pingsenderfactory.h | 7 +- client/platforms/linux/linuxpingsender.cpp | 185 ++++++++++++++++++ client/platforms/linux/linuxpingsender.h | 39 ++++ .../platforms/windows/windowspingsender.cpp | 1 + service/server/CMakeLists.txt | 2 + 11 files changed, 304 insertions(+), 16 deletions(-) create mode 100644 client/platforms/linux/linuxpingsender.cpp create mode 100644 client/platforms/linux/linuxpingsender.h diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index c3af531a..be85b1d1 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -33,6 +33,10 @@ set(HEADERS ${HEADERS} # Mozilla headres set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/mozilla/models/server.h + ${CLIENT_ROOT_DIR}/mozilla/dnspingsender.h + ${CLIENT_ROOT_DIR}/mozilla/pinghelper.h + ${CLIENT_ROOT_DIR}/mozilla/pingsender.h + ${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.h ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.h ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.h ${CLIENT_ROOT_DIR}/mozilla/controllerimpl.h @@ -84,6 +88,10 @@ set(SOURCES ${SOURCES} # Mozilla sources set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp + ${CLIENT_ROOT_DIR}/mozilla/dnspingsender.cpp + ${CLIENT_ROOT_DIR}/mozilla/pinghelper.cpp + ${CLIENT_ROOT_DIR}/mozilla/pingsender.cpp + ${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp @@ -147,13 +155,25 @@ set(SOURCES ${SOURCES} ${UI_CONTROLLERS_CPP} ) +if (LINUX) + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.h + ) + + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.cpp + ) +endif() + if(WIN32) set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h + ${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.h ) set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp + ${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.cpp ) set(RESOURCES ${RESOURCES} @@ -161,6 +181,16 @@ if(WIN32) ) endif() +if (APPLE AND NOT IOS AND NOT MACOS_NE) + set(HEADERS ${HEADERS} + ${CLIENT_ROOT_DIR}/platforms/macos/macospingsender.h + ) + + set(SOURCES ${SOURCES} + ${CLIENT_ROOT_DIR}/platforms/macos/macosspingsender.cpp + ) +endif() + if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) message("Client desktop build") add_compile_definitions(AMNEZIA_DESKTOP) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 1081bcae..31060068 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "ipaddress.h" #include "leakdetector.h" @@ -48,6 +49,15 @@ LocalSocketController::LocalSocketController() { m_initializingTimer.setSingleShot(true); connect(&m_initializingTimer, &QTimer::timeout, this, &LocalSocketController::initializeInternal); + + connect(&m_pingHelper, &PingHelper::connectionLose, this, [this]() { + logger.debug() << "Connection Lose"; + m_pingHelper.stop(); + this->deactivate(); + QThread::msleep(3000); + this->activate(m_RawConfig); + }); + } LocalSocketController::~LocalSocketController() { @@ -116,6 +126,8 @@ void LocalSocketController::daemonConnected() { void LocalSocketController::activate(const QJsonObject &rawConfig) { + m_RawConfig = rawConfig; + QString protocolName = rawConfig.value("protocol").toString(); int splitTunnelType = rawConfig.value("splitTunnelType").toInt(); @@ -258,6 +270,7 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); } + write(json); } @@ -362,6 +375,8 @@ void LocalSocketController::parseCommand(const QByteArray& command) { return; } + qDebug() << command; + QJsonObject obj = json.object(); QJsonValue typeValue = obj.value("type"); if (!typeValue.isString()) { @@ -406,6 +421,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) { } if (type == "status") { + QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway"); if (!serverIpv4Gateway.isString()) { logger.error() << "Unexpected serverIpv4Gateway value"; @@ -418,6 +434,8 @@ void LocalSocketController::parseCommand(const QByteArray& command) { return; } + m_pingHelper.start(serverIpv4Gateway.toString(), deviceIpv4Address.toString()); + QJsonValue txBytes = obj.value("txBytes"); if (!txBytes.isDouble()) { logger.error() << "Unexpected txBytes value"; @@ -451,6 +469,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) { logger.debug() << "Handshake completed with:" << pubkey.toString(); emit connected(pubkey.toString()); + checkStatus(); return; } diff --git a/client/mozilla/localsocketcontroller.h b/client/mozilla/localsocketcontroller.h index f0652e5a..e3d1f7b1 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -11,6 +11,9 @@ #include #include "controllerimpl.h" +#include "mozilla/pinghelper.h" +#include "qjsonobject.h" + class QJsonObject; @@ -60,7 +63,11 @@ class LocalSocketController final : public ControllerImpl { std::function m_logCallback = nullptr; + QJsonObject m_RawConfig; + PingHelper m_pingHelper; + QTimer m_initializingTimer; + QTimer m_statusTimer; uint32_t m_initializingRetry = 0; }; 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/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/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/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 28174774..494e4bed 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -212,6 +212,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 +227,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 From 8bb4fa3f35e7dd1aa0d7c05cf8f37acae733b640 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 13 Mar 2025 17:43:13 +0200 Subject: [PATCH 02/14] Use service for PingSender --- client/cmake/sources.cmake | 34 ++---------------------- client/mozilla/localsocketcontroller.cpp | 27 ++++++++++++------- client/mozilla/localsocketcontroller.h | 2 -- ipc/ipc_interface.rep | 4 +++ ipc/ipcserver.cpp | 16 ++++++++++- ipc/ipcserver.h | 8 ++++++ service/server/CMakeLists.txt | 2 -- 7 files changed, 46 insertions(+), 47 deletions(-) diff --git a/client/cmake/sources.cmake b/client/cmake/sources.cmake index be85b1d1..49504e3c 100644 --- a/client/cmake/sources.cmake +++ b/client/cmake/sources.cmake @@ -33,14 +33,9 @@ set(HEADERS ${HEADERS} # Mozilla headres set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/mozilla/models/server.h - ${CLIENT_ROOT_DIR}/mozilla/dnspingsender.h - ${CLIENT_ROOT_DIR}/mozilla/pinghelper.h - ${CLIENT_ROOT_DIR}/mozilla/pingsender.h - ${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.h ${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) @@ -88,13 +83,8 @@ set(SOURCES ${SOURCES} # Mozilla sources set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/mozilla/models/server.cpp - ${CLIENT_ROOT_DIR}/mozilla/dnspingsender.cpp - ${CLIENT_ROOT_DIR}/mozilla/pinghelper.cpp - ${CLIENT_ROOT_DIR}/mozilla/pingsender.cpp - ${CLIENT_ROOT_DIR}/mozilla/pingsenderfactory.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/ipaddress.cpp ${CLIENT_ROOT_DIR}/mozilla/shared/leakdetector.cpp - ${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp ) if(NOT IOS) @@ -155,25 +145,13 @@ set(SOURCES ${SOURCES} ${UI_CONTROLLERS_CPP} ) -if (LINUX) - set(HEADERS ${HEADERS} - ${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.h - ) - - set(SOURCES ${SOURCES} - ${CLIENT_ROOT_DIR}/platforms/linux/linuxpingsender.cpp - ) -endif() - if(WIN32) set(HEADERS ${HEADERS} ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.h - ${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.h ) set(SOURCES ${SOURCES} ${CLIENT_ROOT_DIR}/protocols/ikev2_vpn_protocol_windows.cpp - ${CLIENT_ROOT_DIR}/platforms/windows/windowspingsender.cpp ) set(RESOURCES ${RESOURCES} @@ -181,16 +159,6 @@ if(WIN32) ) endif() -if (APPLE AND NOT IOS AND NOT MACOS_NE) - set(HEADERS ${HEADERS} - ${CLIENT_ROOT_DIR}/platforms/macos/macospingsender.h - ) - - set(SOURCES ${SOURCES} - ${CLIENT_ROOT_DIR}/platforms/macos/macosspingsender.cpp - ) -endif() - if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID)) message("Client desktop build") add_compile_definitions(AMNEZIA_DESKTOP) @@ -205,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/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 31060068..57cfdcb7 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -21,6 +21,8 @@ #include "models/server.h" #include "daemon/daemonerrors.h" +#include "core/ipcclient.h" + #include "protocols/protocols_defs.h" // How many times do we try to reconnect. @@ -39,7 +41,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); @@ -50,11 +52,12 @@ LocalSocketController::LocalSocketController() { connect(&m_initializingTimer, &QTimer::timeout, this, &LocalSocketController::initializeInternal); - connect(&m_pingHelper, &PingHelper::connectionLose, this, [this]() { + connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose, + this, [this]() { logger.debug() << "Connection Lose"; - m_pingHelper.stop(); + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); this->deactivate(); - QThread::msleep(3000); this->activate(m_RawConfig); }); @@ -145,11 +148,14 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip)); // 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)); @@ -286,6 +292,8 @@ void LocalSocketController::deactivate() { QJsonObject json; json.insert("type", "deactivate"); write(json); + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); emit disconnected(); } @@ -375,8 +383,6 @@ void LocalSocketController::parseCommand(const QByteArray& command) { return; } - qDebug() << command; - QJsonObject obj = json.object(); QJsonValue typeValue = obj.value("type"); if (!typeValue.isString()) { @@ -434,7 +440,8 @@ void LocalSocketController::parseCommand(const QByteArray& command) { return; } - m_pingHelper.start(serverIpv4Gateway.toString(), deviceIpv4Address.toString()); + IpcClient::Interface()->startNetworkCheck(serverIpv4Gateway.toString(), + deviceIpv4Address.toString()); QJsonValue txBytes = obj.value("txBytes"); if (!txBytes.isDouble()) { diff --git a/client/mozilla/localsocketcontroller.h b/client/mozilla/localsocketcontroller.h index e3d1f7b1..3fea8ac9 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -11,7 +11,6 @@ #include #include "controllerimpl.h" -#include "mozilla/pinghelper.h" #include "qjsonobject.h" @@ -64,7 +63,6 @@ class LocalSocketController final : public ControllerImpl { std::function m_logCallback = nullptr; QJsonObject m_RawConfig; - PingHelper m_pingHelper; QTimer m_initializingTimer; QTimer m_statusTimer; diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index c0f031fe..41349647 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -32,5 +32,9 @@ 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() ); }; diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 17f34499..6c43203b 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,20 @@ 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..86311c62 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,11 @@ 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; + +signals: + void ConnectionLose(); private: int m_localpid = 0; @@ -57,6 +63,8 @@ private: }; QMap m_processes; + PingHelper m_pingHelper; + }; #endif // IPCSERVER_H diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 494e4bed..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 From 9b41ed66bb02c7e00be58cd77f3635a123195e19 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 26 Mar 2025 21:11:40 +0200 Subject: [PATCH 03/14] Cleanup unused code --- client/mozilla/localsocketcontroller.cpp | 5 ----- client/mozilla/localsocketcontroller.h | 1 - ipc/ipcserver.cpp | 1 - 3 files changed, 7 deletions(-) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 57cfdcb7..def13221 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -15,10 +15,8 @@ #include #include -#include "ipaddress.h" #include "leakdetector.h" #include "logger.h" -#include "models/server.h" #include "daemon/daemonerrors.h" #include "core/ipcclient.h" @@ -128,7 +126,6 @@ void LocalSocketController::daemonConnected() { } void LocalSocketController::activate(const QJsonObject &rawConfig) { - m_RawConfig = rawConfig; QString protocolName = rawConfig.value("protocol").toString(); @@ -230,7 +227,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert("allowedIPAddressRanges", jsAllowedIPAddesses); - QJsonArray jsExcludedAddresses; jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName)); if (splitTunnelType == 2) { @@ -276,7 +272,6 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) { json.insert(amnezia::config_key::transportPacketMagicHeader, wgConfig.value(amnezia::config_key::transportPacketMagicHeader)); } - write(json); } diff --git a/client/mozilla/localsocketcontroller.h b/client/mozilla/localsocketcontroller.h index 3fea8ac9..79235fb0 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -65,7 +65,6 @@ class LocalSocketController final : public ControllerImpl { QJsonObject m_RawConfig; QTimer m_initializingTimer; - QTimer m_statusTimer; uint32_t m_initializingRetry = 0; }; diff --git a/ipc/ipcserver.cpp b/ipc/ipcserver.cpp index 6c43203b..bd51a686 100644 --- a/ipc/ipcserver.cpp +++ b/ipc/ipcserver.cpp @@ -196,7 +196,6 @@ bool IpcServer::startNetworkCheck(const QString& serverIpv4Gateway, const QStrin bool IpcServer::stopNetworkCheck() { - qDebug() << "stopNetworkCheck"; m_pingHelper.stop(); return true; From 4b864259929c6c1fd76ef6da1cf05832c60cc43a Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 27 Mar 2025 21:06:47 +0200 Subject: [PATCH 04/14] Use networkchecker for all protocols --- client/mozilla/localsocketcontroller.cpp | 23 +++++++++-------------- client/mozilla/localsocketcontroller.h | 3 +-- client/protocols/vpnprotocol.cpp | 5 +++++ client/protocols/vpnprotocol.h | 1 + client/protocols/wireguardprotocol.cpp | 7 +++++++ client/vpnconnection.cpp | 18 ++++++++++++++++++ client/vpnconnection.h | 4 ++++ 7 files changed, 45 insertions(+), 16 deletions(-) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index def13221..678c0a08 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -50,14 +50,7 @@ LocalSocketController::LocalSocketController() { connect(&m_initializingTimer, &QTimer::timeout, this, &LocalSocketController::initializeInternal); - connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose, - this, [this]() { - logger.debug() << "Connection Lose"; - auto result = IpcClient::Interface()->stopNetworkCheck(); - result.waitForFinished(3000); - this->deactivate(); - this->activate(m_RawConfig); - }); + } @@ -126,8 +119,6 @@ void LocalSocketController::daemonConnected() { } void LocalSocketController::activate(const QJsonObject &rawConfig) { - m_RawConfig = rawConfig; - QString protocolName = rawConfig.value("protocol").toString(); int splitTunnelType = rawConfig.value("splitTunnelType").toInt(); @@ -143,6 +134,7 @@ 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 @@ -435,9 +427,6 @@ void LocalSocketController::parseCommand(const QByteArray& command) { return; } - IpcClient::Interface()->startNetworkCheck(serverIpv4Gateway.toString(), - deviceIpv4Address.toString()); - QJsonValue txBytes = obj.value("txBytes"); if (!txBytes.isDouble()) { logger.error() << "Unexpected txBytes value"; @@ -470,8 +459,14 @@ void LocalSocketController::parseCommand(const QByteArray& command) { logger.debug() << "Handshake completed with:" << pubkey.toString(); - emit connected(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 79235fb0..4bf2addb 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -60,10 +60,9 @@ class LocalSocketController final : public ControllerImpl { QByteArray m_buffer; + QString m_deviceIpv4; std::function m_logCallback = nullptr; - QJsonObject m_RawConfig; - QTimer m_initializingTimer; uint32_t m_initializingRetry = 0; }; 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..c22fbaea 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -86,8 +86,13 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } } + if (container != DockerContainer::Ipsec) { + IpcClient::Interface()->startNetworkCheck(remoteAddress(), m_vpnProtocol->vpnLocalAddress()); + } + } else if (state == Vpn::ConnectionState::Error) { IpcClient::Interface()->flushDns(); + IpcClient::Interface()->stopNetworkCheck(); if (m_settings->isSitesSplitTunnelingEnabled()) { if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -97,6 +102,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } else if (state == Vpn::ConnectionState::Connecting) { } else if (state == Vpn::ConnectionState::Disconnected) { + IpcClient::Interface()->stopNetworkCheck(); } } #endif @@ -237,6 +243,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) { @@ -281,6 +290,15 @@ void VpnConnection::createProtocolConnections() 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))); + + connect(IpcClient::Interface().data(), &IpcInterfaceReplica::connectionLose, + this, [this]() { + qDebug() << "Connection Lose"; + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); + this->disconnectFromVpn(); + this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); + }); } void VpnConnection::appendKillSwitchConfig() diff --git a/client/vpnconnection.h b/client/vpnconnection.h index 0160edce..f590a07e 100644 --- a/client/vpnconnection.h +++ b/client/vpnconnection.h @@ -77,6 +77,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; From 681eb5aa86886509e173f1ebc3598512de54d43a Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 27 Mar 2025 21:28:14 +0200 Subject: [PATCH 05/14] fix android build --- client/vpnconnection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index c22fbaea..c4311d63 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -54,7 +54,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()); @@ -291,6 +290,7 @@ void VpnConnection::createProtocolConnections() 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"; @@ -299,6 +299,7 @@ void VpnConnection::createProtocolConnections() this->disconnectFromVpn(); this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); }); +#endif } void VpnConnection::appendKillSwitchConfig() From 26994c21b1073eb6fa1794e57d1d35aab74a19fc Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 27 Mar 2025 21:46:10 +0200 Subject: [PATCH 06/14] add delay for ping checker stop --- client/mozilla/localsocketcontroller.cpp | 9 +-------- client/mozilla/localsocketcontroller.h | 1 - client/vpnconnection.cpp | 8 ++++---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index 678c0a08..dced732d 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -49,9 +49,6 @@ LocalSocketController::LocalSocketController() { m_initializingTimer.setSingleShot(true); connect(&m_initializingTimer, &QTimer::timeout, this, &LocalSocketController::initializeInternal); - - - } LocalSocketController::~LocalSocketController() { @@ -279,8 +276,6 @@ void LocalSocketController::deactivate() { QJsonObject json; json.insert("type", "deactivate"); write(json); - auto result = IpcClient::Interface()->stopNetworkCheck(); - result.waitForFinished(3000); emit disconnected(); } @@ -462,9 +457,7 @@ void LocalSocketController::parseCommand(const QByteArray& command) { checkStatus(); - emit statusUpdated("", - m_deviceIpv4, 0, - 0); + emit statusUpdated("", m_deviceIpv4, 0, 0); emit connected(pubkey.toString()); return; diff --git a/client/mozilla/localsocketcontroller.h b/client/mozilla/localsocketcontroller.h index 4bf2addb..4070c752 100644 --- a/client/mozilla/localsocketcontroller.h +++ b/client/mozilla/localsocketcontroller.h @@ -11,7 +11,6 @@ #include #include "controllerimpl.h" -#include "qjsonobject.h" class QJsonObject; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index c4311d63..d9e7c9d7 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 @@ -91,7 +89,8 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } else if (state == Vpn::ConnectionState::Error) { IpcClient::Interface()->flushDns(); - IpcClient::Interface()->stopNetworkCheck(); + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); if (m_settings->isSitesSplitTunnelingEnabled()) { if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -101,7 +100,8 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } else if (state == Vpn::ConnectionState::Connecting) { } else if (state == Vpn::ConnectionState::Disconnected) { - IpcClient::Interface()->stopNetworkCheck(); + auto result = IpcClient::Interface()->stopNetworkCheck(); + result.waitForFinished(3000); } } #endif From 517930dd22361bd1655918703c8e7d650550dea5 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 27 Mar 2025 22:53:06 +0200 Subject: [PATCH 07/14] handle for interafe problems on windows --- client/mozilla/localsocketcontroller.cpp | 3 --- client/platforms/windows/windowspingsender.cpp | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/mozilla/localsocketcontroller.cpp b/client/mozilla/localsocketcontroller.cpp index dced732d..dfdc03a3 100644 --- a/client/mozilla/localsocketcontroller.cpp +++ b/client/mozilla/localsocketcontroller.cpp @@ -13,14 +13,11 @@ #include #include #include -#include #include "leakdetector.h" #include "logger.h" #include "daemon/daemonerrors.h" -#include "core/ipcclient.h" - #include "protocols/protocols_defs.h" // How many times do we try to reconnect. diff --git a/client/platforms/windows/windowspingsender.cpp b/client/platforms/windows/windowspingsender.cpp index a39ac2de..3e8113cb 100644 --- a/client/platforms/windows/windowspingsender.cpp +++ b/client/platforms/windows/windowspingsender.cpp @@ -161,6 +161,9 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { DWORD status = GetLastError(); if (status != ERROR_IO_PENDING) { QString errmsg = WindowsUtils::getErrorMessage(); + if (status == ERROR_INVALID_NETNAME) { + emit criticalPingError(); + } logger.error() << "failed to start Code: " << status << " Message: " << errmsg << " dest:" << logger.sensitive(dest.toString()); From e7305215761429787d833d9356488cc21cf25d5f Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sat, 29 Mar 2025 14:43:19 +0200 Subject: [PATCH 08/14] Restart IpcClient after OS suspend --- client/core/ipcclient.cpp | 6 ++++++ client/core/ipcclient.h | 1 + client/platforms/windows/windowspingsender.cpp | 3 --- client/vpnconnection.cpp | 7 +++++-- 4 files changed, 12 insertions(+), 5 deletions(-) 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/platforms/windows/windowspingsender.cpp b/client/platforms/windows/windowspingsender.cpp index 3e8113cb..a39ac2de 100644 --- a/client/platforms/windows/windowspingsender.cpp +++ b/client/platforms/windows/windowspingsender.cpp @@ -161,9 +161,6 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { DWORD status = GetLastError(); if (status != ERROR_IO_PENDING) { QString errmsg = WindowsUtils::getErrorMessage(); - if (status == ERROR_INVALID_NETNAME) { - emit criticalPingError(); - } logger.error() << "failed to start Code: " << status << " Message: " << errmsg << " dest:" << logger.sensitive(dest.toString()); diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index d9e7c9d7..ad2b591c 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -89,8 +89,6 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } else if (state == Vpn::ConnectionState::Error) { IpcClient::Interface()->flushDns(); - auto result = IpcClient::Interface()->stopNetworkCheck(); - result.waitForFinished(3000); if (m_settings->isSitesSplitTunnelingEnabled()) { if (m_settings->routeMode() == Settings::VpnOnlyForwardSites) { @@ -224,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); } From 319043818a75039ed13003375df5a090baaead0a Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 2 Apr 2025 11:33:15 +0300 Subject: [PATCH 09/14] Add DBus network checker for Linux --- client/mozilla/networkwatcher.cpp | 4 ++- client/mozilla/networkwatcher.h | 1 + client/mozilla/networkwatcherimpl.h | 2 ++ .../platforms/linux/linuxnetworkwatcher.cpp | 3 ++ .../linux/linuxnetworkwatcherworker.cpp | 33 ++++++++++++++++++- .../linux/linuxnetworkwatcherworker.h | 2 ++ client/vpnconnection.cpp | 14 ++++++-- client/vpnconnection.h | 1 + ipc/ipc_interface.rep | 3 +- ipc/ipcserver.h | 3 -- service/server/localserver.cpp | 6 ++-- service/server/localserver.h | 4 ++- 12 files changed, 65 insertions(+), 11 deletions(-) diff --git a/client/mozilla/networkwatcher.cpp b/client/mozilla/networkwatcher.cpp index 59caf1f2..5d7716f6 100644 --- a/client/mozilla/networkwatcher.cpp +++ b/client/mozilla/networkwatcher.cpp @@ -51,7 +51,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); @@ -73,6 +73,8 @@ void NetworkWatcher::initialize() { &NetworkWatcher::unsecuredNetwork); connect(m_impl, &NetworkWatcherImpl::networkChanged, this, &NetworkWatcher::networkChange); + connect(m_impl, &NetworkWatcherImpl::sleepMode, this, + &NetworkWatcher::sleepMode); m_impl->initialize(); diff --git a/client/mozilla/networkwatcher.h b/client/mozilla/networkwatcher.h index 43536dc3..4f86e2a4 100644 --- a/client/mozilla/networkwatcher.h +++ b/client/mozilla/networkwatcher.h @@ -33,6 +33,7 @@ public: 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/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/vpnconnection.cpp b/client/vpnconnection.cpp index ad2b591c..9978ddc0 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -286,6 +286,12 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede emit connectionStateChanged(Vpn::ConnectionState::Error); } +void VpnConnection::restartConnection() +{ + this->disconnectFromVpn(); + this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); +} + void VpnConnection::createProtocolConnections() { connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError); @@ -299,8 +305,12 @@ void VpnConnection::createProtocolConnections() qDebug() << "Connection Lose"; auto result = IpcClient::Interface()->stopNetworkCheck(); result.waitForFinished(3000); - this->disconnectFromVpn(); - this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); + this->restartConnection(); + }); + connect(IpcClient::Interface().data(), &IpcInterfaceReplica::networkChange, + this, [this]() { + qDebug() << "Network change"; + this->restartConnection(); }); #endif } diff --git a/client/vpnconnection.h b/client/vpnconnection.h index f590a07e..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); diff --git a/ipc/ipc_interface.rep b/ipc/ipc_interface.rep index 41349647..1f4bcde9 100644 --- a/ipc/ipc_interface.rep +++ b/ipc/ipc_interface.rep @@ -35,6 +35,7 @@ class IpcInterface SLOT( bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) ); SLOT( bool stopNetworkCheck() ); - SIGNAL( connectionLose() ); + SIGNAL( connectionLose() ); + SIGNAL( networkChange() ); }; diff --git a/ipc/ipcserver.h b/ipc/ipcserver.h index 86311c62..b4129f2d 100644 --- a/ipc/ipcserver.h +++ b/ipc/ipcserver.h @@ -42,9 +42,6 @@ public: virtual bool startNetworkCheck(const QString& serverIpv4Gateway, const QString& deviceIpv4Address) override; virtual bool stopNetworkCheck() override; -signals: - void ConnectionLose(); - private: int m_localpid = 0; diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 8a5079cb..17869705 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,10 @@ 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; From eff460b227294f12cd12a977ecf4072a5d44dc91 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 2 Apr 2025 20:26:59 +0300 Subject: [PATCH 10/14] Use ping check for tun interfce --- client/vpnconnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 9978ddc0..eca6be68 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -84,7 +84,7 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state) } if (container != DockerContainer::Ipsec) { - IpcClient::Interface()->startNetworkCheck(remoteAddress(), m_vpnProtocol->vpnLocalAddress()); + IpcClient::Interface()->startNetworkCheck(m_vpnProtocol->vpnLocalAddress(), m_vpnProtocol->vpnLocalAddress()); } } else if (state == Vpn::ConnectionState::Error) { From f767171c06a5583a538651d070710c2e88b326d4 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 3 Apr 2025 20:41:46 +0300 Subject: [PATCH 11/14] Windows suspend mode handler --- client/mozilla/networkwatcher.cpp | 13 ++++-- client/mozilla/networkwatcher.h | 2 + .../windows/daemon/windowsdaemon.cpp | 1 - .../windows/windowsnetworkwatcher.cpp | 40 ++++++++++++++++++- .../platforms/windows/windowsnetworkwatcher.h | 1 + service/server/localserver.cpp | 1 - 6 files changed, 51 insertions(+), 7 deletions(-) diff --git a/client/mozilla/networkwatcher.cpp b/client/mozilla/networkwatcher.cpp index 5d7716f6..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 @@ -69,16 +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::sleepMode); - + &NetworkWatcher::onSleepMode); m_impl->initialize(); + // TODO: IMPL FOR AMNEZIA #if 0 SettingsHolder* settingsHolder = SettingsHolder::instance(); @@ -119,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 4f86e2a4..f28e74fb 100644 --- a/client/mozilla/networkwatcher.h +++ b/client/mozilla/networkwatcher.h @@ -29,6 +29,8 @@ public: // false to restore. void simulateDisconnection(bool simulatedDisconnection); + void onSleepMode(); + QNetworkInformation::Reachability getReachability(); signals: 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/service/server/localserver.cpp b/service/server/localserver.cpp index 17869705..56a817d0 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -46,7 +46,6 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), } m_networkWatcher.initialize(); - connect(&m_networkWatcher, &NetworkWatcher::sleepMode, &m_ipcServer, &IpcServer::networkChange); #ifdef Q_OS_LINUX From 5ef8254cba6803fb3a05e726006f63f7309f493c Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Thu, 3 Apr 2025 22:12:54 +0300 Subject: [PATCH 12/14] MacOS suspend mode handler draft --- client/platforms/macos/macosnetworkwatcher.h | 21 +++++ client/platforms/macos/macosnetworkwatcher.mm | 89 +++++++++++++++++++ 2 files changed, 110 insertions(+) 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) { From 62d9bcaf7fd548450417a954e982031598860b29 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 6 Apr 2025 20:30:52 +0300 Subject: [PATCH 13/14] Add delay for Linux wakeup reconnect --- client/vpnconnection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index eca6be68..62c296ae 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -289,6 +289,9 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede void VpnConnection::restartConnection() { this->disconnectFromVpn(); +#ifdef (Q_OS_LINUX) + QThread::msleep(5000); +#endif this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration); } From 71691fa01e1b39be177de3ca4dc1bb8bd4a8bcae Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 6 Apr 2025 20:50:37 +0300 Subject: [PATCH 14/14] Add delay for Linux wakeup reconnect --- client/vpnconnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 62c296ae..24f2a699 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -289,7 +289,7 @@ void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &crede void VpnConnection::restartConnection() { this->disconnectFromVpn(); -#ifdef (Q_OS_LINUX) +#ifdef Q_OS_LINUX QThread::msleep(5000); #endif this->connectToVpn(m_serverIndex, m_serverCredentials, m_dockerContainer, m_vpnConfiguration);