diff --git a/client/mozilla/networkwatcher.cpp b/client/mozilla/networkwatcher.cpp index 47fdb622..59caf1f2 100644 --- a/client/mozilla/networkwatcher.cpp +++ b/client/mozilla/networkwatcher.cpp @@ -171,9 +171,15 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName, } -QString NetworkWatcher::getCurrentTransport() { - auto type = m_impl->getTransportType(); - QMetaEnum metaEnum = QMetaEnum::fromType(); - return QString(metaEnum.valueToKey(type)) - .remove("TransportType_", Qt::CaseSensitive); +QNetworkInformation::Reachability NetworkWatcher::getReachability() { + if (m_simulatedDisconnection) { + return QNetworkInformation::Reachability::Disconnected; + } else if (QNetworkInformation::instance()) { + return QNetworkInformation::instance()->reachability(); + } + return QNetworkInformation::Reachability::Unknown; +} + +void NetworkWatcher::simulateDisconnection(bool simulatedDisconnection) { + m_simulatedDisconnection = simulatedDisconnection; } diff --git a/client/mozilla/networkwatcher.h b/client/mozilla/networkwatcher.h index 7c30416e..43536dc3 100644 --- a/client/mozilla/networkwatcher.h +++ b/client/mozilla/networkwatcher.h @@ -7,45 +7,50 @@ #include #include -#include +#include + class NetworkWatcherImpl; // This class watches for network changes to detect unsecured wifi. class NetworkWatcher final : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(NetworkWatcher) + Q_OBJECT + Q_DISABLE_COPY_MOVE(NetworkWatcher) - public: - NetworkWatcher(); - ~NetworkWatcher(); +public: + NetworkWatcher(); + ~NetworkWatcher(); - void initialize(); + void initialize(); - // public for the inspector. - void unsecuredNetwork(const QString& networkName, const QString& networkId); + // Public for the Inspector. + void unsecuredNetwork(const QString& networkName, const QString& networkId); + // Used for the Inspector. simulateOffline = true to mock being disconnected, + // false to restore. + void simulateDisconnection(bool simulatedDisconnection); - QString getCurrentTransport(); + QNetworkInformation::Reachability getReachability(); - signals: - void networkChange(); +signals: + void networkChange(); - private: - void settingsChanged(); +private: + void settingsChanged(); - // void notificationClicked(NotificationHandler::Message message); +private: + bool m_active = false; + bool m_reportUnsecuredNetwork = false; - private: - bool m_active = false; - bool m_reportUnsecuredNetwork = false; + // Platform-specific implementation. + NetworkWatcherImpl* m_impl = nullptr; - // Platform-specific implementation. - NetworkWatcherImpl* m_impl = nullptr; + QMap m_networks; - QMap m_networks; + // This is used to connect NotificationHandler lazily. + bool m_firstNotification = true; - // This is used to connect NotificationHandler lazily. - bool m_firstNotification = true; + // Used to simulate network disconnection in the Inspector + bool m_simulatedDisconnection = false; }; #endif // NETWORKWATCHER_H diff --git a/client/mozilla/networkwatcherimpl.h b/client/mozilla/networkwatcherimpl.h index d156c0db..78117dca 100644 --- a/client/mozilla/networkwatcherimpl.h +++ b/client/mozilla/networkwatcherimpl.h @@ -5,50 +5,45 @@ #ifndef NETWORKWATCHERIMPL_H #define NETWORKWATCHERIMPL_H +#include #include class NetworkWatcherImpl : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(NetworkWatcherImpl) + Q_OBJECT + Q_DISABLE_COPY_MOVE(NetworkWatcherImpl) - public: - NetworkWatcherImpl(QObject* parent) : QObject(parent) {} +public: + NetworkWatcherImpl(QObject* parent) : QObject(parent) {} - virtual ~NetworkWatcherImpl() = default; + virtual ~NetworkWatcherImpl() = default; - virtual void initialize() = 0; + virtual void initialize() = 0; - virtual void start() { m_active = true; } - virtual void stop() { m_active = false; } + virtual void start() { m_active = true; } + virtual void stop() { m_active = false; } - bool isActive() const { return m_active; } + bool isActive() const { return m_active; } - enum TransportType { - TransportType_Unknown = 0, - TransportType_Ethernet = 1, - TransportType_WiFi = 2, - TransportType_Cellular = 3, // In Case the API does not retun the gsm type - TransportType_Other = 4, // I.e USB thethering - TransportType_None = 5 // I.e Airplane Mode or no active network device - }; - Q_ENUM(TransportType); + enum TransportType { + TransportType_Unknown = 0, + TransportType_Ethernet = 1, + TransportType_WiFi = 2, + TransportType_Cellular = 3, // In Case the API does not retun the gsm type + TransportType_Other = 4, // I.e USB thethering + TransportType_None = 5 // I.e Airplane Mode or no active network device + }; + Q_ENUM(TransportType); - // Returns the current type of Network Connection - virtual TransportType getTransportType() = 0; +signals: + // Fires when the Device Connects to an unsecured Network + void unsecuredNetwork(const QString& networkName, const QString& networkId); + // Fires on when the connected WIFI Changes + // TODO: Only windows-networkwatcher has this, the other plattforms should + // too. + void networkChanged(QString newBSSID); - signals: - // Fires when the Device Connects to an unsecured Network - void unsecuredNetwork(const QString& networkName, const QString& networkId); - // Fires on when the connected WIFI Changes - // TODO: Only windows-networkwatcher has this, the other plattforms should - // too. - void networkChanged(QString newBSSID); - - // Fired when the Device changed the Type of Transport - void transportChanged(NetworkWatcherImpl::TransportType transportType); - - private: - bool m_active = false; +private: + bool m_active = false; }; #endif // NETWORKWATCHERIMPL_H diff --git a/client/platforms/dummy/dummynetworkwatcher.cpp b/client/platforms/dummy/dummynetworkwatcher.cpp index b4291494..fd7db344 100644 --- a/client/platforms/dummy/dummynetworkwatcher.cpp +++ b/client/platforms/dummy/dummynetworkwatcher.cpp @@ -8,11 +8,11 @@ DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent) : NetworkWatcherImpl(parent) { - MZ_COUNT_CTOR(DummyNetworkWatcher); + MZ_COUNT_CTOR(DummyNetworkWatcher); } DummyNetworkWatcher::~DummyNetworkWatcher() { - MZ_COUNT_DTOR(DummyNetworkWatcher); + MZ_COUNT_DTOR(DummyNetworkWatcher); } void DummyNetworkWatcher::initialize() {} diff --git a/client/platforms/dummy/dummynetworkwatcher.h b/client/platforms/dummy/dummynetworkwatcher.h index 4e6d2a66..7374ab74 100644 --- a/client/platforms/dummy/dummynetworkwatcher.h +++ b/client/platforms/dummy/dummynetworkwatcher.h @@ -8,15 +8,11 @@ #include "networkwatcherimpl.h" class DummyNetworkWatcher final : public NetworkWatcherImpl { - public: - DummyNetworkWatcher(QObject* parent); - ~DummyNetworkWatcher(); +public: + DummyNetworkWatcher(QObject* parent); + ~DummyNetworkWatcher(); - void initialize() override; - - NetworkWatcherImpl::TransportType getTransportType() override { - return TransportType_Other; - }; + void initialize() override; }; #endif // DUMMYNETWORKWATCHER_H diff --git a/client/platforms/ios/iosnetworkwatcher.h b/client/platforms/ios/iosnetworkwatcher.h index 5b779a87..70609e1f 100644 --- a/client/platforms/ios/iosnetworkwatcher.h +++ b/client/platforms/ios/iosnetworkwatcher.h @@ -15,7 +15,6 @@ class IOSNetworkWatcher : public NetworkWatcherImpl { ~IOSNetworkWatcher(); void initialize() override; - NetworkWatcherImpl::TransportType getTransportType() override; private: NetworkWatcherImpl::TransportType toTransportType(nw_path_t path); diff --git a/client/platforms/ios/iosnetworkwatcher.mm b/client/platforms/ios/iosnetworkwatcher.mm index 9c6ca411..720b303b 100644 --- a/client/platforms/ios/iosnetworkwatcher.mm +++ b/client/platforms/ios/iosnetworkwatcher.mm @@ -37,16 +37,6 @@ void IOSNetworkWatcher::initialize() { //TODO IMPL FOR AMNEZIA } -NetworkWatcherImpl::TransportType IOSNetworkWatcher::getTransportType() { - //TODO IMPL FOR AMNEZIA - - if (m_observableConnection != nil) { - return m_currentVPNTransport; - } - // If we don't have an open tunnel-observer, m_currentVPNTransport is probably wrong. - return NetworkWatcherImpl::TransportType_Unknown; -} - NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) { if (path == nil) { return NetworkWatcherImpl::TransportType_Unknown; diff --git a/client/platforms/linux/linuxnetworkwatcher.h b/client/platforms/linux/linuxnetworkwatcher.h old mode 100644 new mode 100755 index ed8c76ba..d67602f8 --- a/client/platforms/linux/linuxnetworkwatcher.h +++ b/client/platforms/linux/linuxnetworkwatcher.h @@ -22,11 +22,6 @@ class LinuxNetworkWatcher final : public NetworkWatcherImpl { void start() override; - NetworkWatcherImpl::TransportType getTransportType() { - // TODO: Find out how to do that on linux generally. (VPN-2382) - return NetworkWatcherImpl::TransportType_Unknown; - }; - signals: void checkDevicesInThread(); diff --git a/client/platforms/macos/daemon/macosroutemonitor.cpp b/client/platforms/macos/daemon/macosroutemonitor.cpp index 9f1da4ec..395f008a 100644 --- a/client/platforms/macos/daemon/macosroutemonitor.cpp +++ b/client/platforms/macos/daemon/macosroutemonitor.cpp @@ -95,6 +95,11 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm, !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { return; } + // Ignore interface-scoped routes, we want to find the default route to the + // internet in the global scope. + if (rtm->rtm_flags & RTF_IFSCOPE) { + return; + } // Check for a default route, which should have a netmask of zero. const struct sockaddr* sa = @@ -156,6 +161,11 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm, !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { return; } + // Ignore interface-scoped routes, we want to find the default route to the + // internet in the global scope. + if (rtm->rtm_flags & RTF_IFSCOPE) { + return; + } // Ignore route changes that we caused, or routes on the tunnel interface. if (rtm->rtm_index == m_ifindex) { return; diff --git a/client/platforms/macos/macospingsender.cpp b/client/platforms/macos/macospingsender.cpp index 3b5a09b0..8f108259 100644 --- a/client/platforms/macos/macospingsender.cpp +++ b/client/platforms/macos/macospingsender.cpp @@ -83,7 +83,7 @@ void MacOSPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { packet.icmp_seq = htons(sequence); packet.icmp_cksum = inetChecksum(&packet, sizeof(packet)); - if (sendto(m_socket, (char*)&packet, sizeof(packet), 0, + if (sendto(m_socket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL, (struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) { logger.error() << "ping sending failed:" << strerror(errno); emit criticalPingError(); @@ -107,9 +107,9 @@ void MacOSPingSender::socketReady() { iov.iov_base = packet; iov.iov_len = IP_MAXPACKET; - ssize_t rc = recvmsg(m_socket, &msg, MSG_DONTWAIT); + ssize_t rc = recvmsg(m_socket, &msg, MSG_DONTWAIT | MSG_NOSIGNAL); if (rc <= 0) { - logger.error() << "Recvmsg failed"; + logger.error() << "Recvmsg failed:" << strerror(errno); return; } diff --git a/client/platforms/macos/macosutils.mm b/client/platforms/macos/macosutils.mm index cbe30583..177ed2a2 100644 --- a/client/platforms/macos/macosutils.mm +++ b/client/platforms/macos/macosutils.mm @@ -41,11 +41,33 @@ void MacOSUtils::enableLoginItem(bool startAtBoot) { Q_ASSERT(appId); NSString* loginItemAppId = - QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString(); - CFStringRef cfs = (__bridge CFStringRef)loginItemAppId; + QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString(); - Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO); - logger.debug() << "Result: " << ok; + // For macOS 13 and beyond, register() and unregister() methods + // are used for managing login items since SMLoginItemSetEnabled() is deprecated. + // For versions prior to macOS 13, SMLoginItemSetEnabled() is used. + if (@available(macOS 13, *)) { + // Use register() or unregister() based on the startAtBoot flag + NSError* error = nil; + + if (startAtBoot) { + if (![[SMAppService mainAppService] registerAndReturnError: & error]) { + logger.error() << "Failed to register Amnezia VPN LoginItem: " << error.localizedDescription; + } else { + logger.debug() << "Amnezia VPN LoginItem registered successfully."; + } + } else { + if (![[SMAppService mainAppService] unregisterAndReturnError: & error]) { + logger.error() << "Failed to unregister Amnezia VPN LoginItem: " << error.localizedDescription; + } else { + logger.debug() << "LoginItem unregistered successfully."; + } + } + } else { + CFStringRef cfs = (__bridge CFStringRef) loginItemAppId; + Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO); + logger.debug() << "Result: " << ok; + } } namespace { diff --git a/client/platforms/windows/daemon/dnsutilswindows.cpp b/client/platforms/windows/daemon/dnsutilswindows.cpp index a6485529..1e807834 100644 --- a/client/platforms/windows/daemon/dnsutilswindows.cpp +++ b/client/platforms/windows/daemon/dnsutilswindows.cpp @@ -4,8 +4,11 @@ #include "dnsutilswindows.h" +#include #include #include +#include +#include #include #include @@ -39,30 +42,27 @@ DnsUtilsWindows::~DnsUtilsWindows() { bool DnsUtilsWindows::updateResolvers(const QString& ifname, const QList& resolvers) { - NET_LUID luid; - if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), &luid) != 0) { + MIB_IF_ROW2 entry; + if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), + &entry.InterfaceLuid) != 0) { logger.error() << "Failed to resolve LUID for" << ifname; return false; } - m_luid = luid.Value; + if (GetIfEntry2(&entry) != NO_ERROR) { + logger.error() << "Failed to resolve interface for" << ifname; + return false; + } + m_luid = entry.InterfaceLuid.Value; logger.debug() << "Configuring DNS for" << ifname; if (m_setInterfaceDnsSettingsProcAddr == nullptr) { - return updateResolversNetsh(resolvers); + return updateResolversNetsh(entry.InterfaceIndex, resolvers); } - return updateResolversWin32(resolvers); + return updateResolversWin32(entry.InterfaceGuid, resolvers); } bool DnsUtilsWindows::updateResolversWin32( - const QList& resolvers) { - GUID guid; - NET_LUID luid; - luid.Value = m_luid; - if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) { - logger.error() << "Failed to resolve GUID"; - return false; - } - + GUID guid, const QList& resolvers) { QStringList v4resolvers; QStringList v6resolvers; for (const QHostAddress& addr : resolvers) { @@ -113,16 +113,8 @@ constexpr const char* netshAddTemplate = "interface %1 add dnsservers name=%2 address=%3 validate=no\r\n"; bool DnsUtilsWindows::updateResolversNetsh( - const QList& resolvers) { + int ifindex, const QList& resolvers) { QProcess netsh; - NET_LUID luid; - NET_IFINDEX ifindex; - luid.Value = m_luid; - if (ConvertInterfaceLuidToIndex(&luid, &ifindex) != NO_ERROR) { - logger.error() << "Failed to resolve GUID"; - return false; - } - netsh.setProgram("netsh"); netsh.start(); if (!netsh.waitForStarted(WINDOWS_NETSH_TIMEOUT_MSEC)) { @@ -166,12 +158,26 @@ bool DnsUtilsWindows::updateResolversNetsh( bool DnsUtilsWindows::restoreResolvers() { if (m_luid == 0) { + // If the DNS hasn't been configured, there is nothing to restore. return true; } + MIB_IF_ROW2 entry; + DWORD error; + entry.InterfaceLuid.Value = m_luid; + error = GetIfEntry2(&entry); + if (error == ERROR_FILE_NOT_FOUND) { + // If the interface no longer exists, there is nothing to restore. + return true; + } + if (error != NO_ERROR) { + logger.error() << "Failed to resolve interface entry:" << error; + return false; + } + QList empty; if (m_setInterfaceDnsSettingsProcAddr == nullptr) { - return updateResolversNetsh(empty); + return updateResolversNetsh(entry.InterfaceIndex, empty); } - return updateResolversWin32(empty); + return updateResolversWin32(entry.InterfaceGuid, empty); } diff --git a/client/platforms/windows/daemon/dnsutilswindows.h b/client/platforms/windows/daemon/dnsutilswindows.h index 7d0573e4..f9b7e94b 100644 --- a/client/platforms/windows/daemon/dnsutilswindows.h +++ b/client/platforms/windows/daemon/dnsutilswindows.h @@ -27,8 +27,8 @@ class DnsUtilsWindows final : public DnsUtils { quint64 m_luid = 0; DWORD (*m_setInterfaceDnsSettingsProcAddr)(GUID, const void*) = nullptr; - bool updateResolversWin32(const QList& resolvers); - bool updateResolversNetsh(const QList& resolvers); + bool updateResolversWin32(GUID, const QList& resolvers); + bool updateResolversNetsh(int ifindex, const QList& resolvers); }; #endif // DNSUTILSWINDOWS_H diff --git a/client/platforms/windows/daemon/windowsdaemon.h b/client/platforms/windows/daemon/windowsdaemon.h index 782c6814..d3e81a18 100644 --- a/client/platforms/windows/daemon/windowsdaemon.h +++ b/client/platforms/windows/daemon/windowsdaemon.h @@ -38,7 +38,6 @@ class WindowsDaemon final : public Daemon { Inactive, }; - State m_state = Inactive; int m_inetAdapterIndex = -1; WireguardUtilsWindows* m_wgutils = nullptr; diff --git a/client/platforms/windows/daemon/windowsroutemonitor.cpp b/client/platforms/windows/daemon/windowsroutemonitor.cpp index e60a9178..69967526 100644 --- a/client/platforms/windows/daemon/windowsroutemonitor.cpp +++ b/client/platforms/windows/daemon/windowsroutemonitor.cpp @@ -114,7 +114,7 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) { void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void* ptable) { PMIB_IPFORWARD_TABLE2 table = reinterpret_cast(ptable); - SOCKADDR_INET nexthop = {0}; + SOCKADDR_INET nexthop = {}; quint64 bestLuid = 0; int bestMatch = -1; ULONG bestMetric = ULONG_MAX; diff --git a/client/platforms/windows/daemon/windowstunnellogger.cpp b/client/platforms/windows/daemon/windowstunnellogger.cpp index 7194f0cd..1d5e26b8 100644 --- a/client/platforms/windows/daemon/windowstunnellogger.cpp +++ b/client/platforms/windows/daemon/windowstunnellogger.cpp @@ -39,7 +39,7 @@ Logger logger("tunnel.dll"); WindowsTunnelLogger::WindowsTunnelLogger(const QString& filename, QObject* parent) - : QObject(parent), m_logfile(filename, this), m_timer(this) { + : QObject(parent), m_timer(this), m_logfile(filename, this) { MZ_COUNT_CTOR(WindowsTunnelLogger); m_startTime = QDateTime::currentMSecsSinceEpoch() * 1000000; diff --git a/client/platforms/windows/daemon/windowstunnelservice.cpp b/client/platforms/windows/daemon/windowstunnelservice.cpp index f873203b..ae358dd4 100644 --- a/client/platforms/windows/daemon/windowstunnelservice.cpp +++ b/client/platforms/windows/daemon/windowstunnelservice.cpp @@ -2,7 +2,7 @@ * 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 "WindowsTunnelService.h" +#include "windowstunnelservice.h" #include @@ -30,12 +30,22 @@ static bool waitForServiceStatus(SC_HANDLE service, DWORD expectedStatus); WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) { MZ_COUNT_CTOR(WindowsTunnelService); + logger.debug() << "WindowsTunnelService created."; m_scm = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); if (m_scm == nullptr) { WindowsUtils::windowsLog("Failed to open SCManager"); } + // Is the service already running? Terminate it. + SC_HANDLE service = + OpenService((SC_HANDLE)m_scm, TUNNEL_SERVICE_NAME, SERVICE_ALL_ACCESS); + if (service != nullptr) { + logger.info() << "Tunnel already exists. Terminating it."; + stopAndDeleteTunnelService(service); + CloseServiceHandle(service); + } + connect(&m_timer, &QTimer::timeout, this, &WindowsTunnelService::timeout); } diff --git a/client/platforms/windows/windowscommons.cpp b/client/platforms/windows/windowscommons.cpp index 395859aa..c0a14dda 100644 --- a/client/platforms/windows/windowscommons.cpp +++ b/client/platforms/windows/windowscommons.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -19,9 +20,9 @@ #include "logger.h" #include "platforms/windows/windowsutils.h" -#define TUNNEL_SERVICE_NAME L"WireGuardTunnel$amnvpn" - constexpr const char* VPN_NAME = "AmneziaVPN"; +constexpr const char* WIREGUARD_DIR = "WireGuard"; +constexpr const char* DATA_DIR = "Data"; namespace { Logger logger("WindowsCommons"); @@ -67,27 +68,67 @@ QString WindowsCommons::tunnelConfigFile() { return QString(); } +// static QString WindowsCommons::tunnelLogFile() { - QStringList paths = - QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + static QString tunnelLogFilePath = getTunnelLogFilePath(); + return tunnelLogFilePath; +} - for (const QString& path : paths) { - QDir dir(path); - if (!dir.exists()) { - continue; +// static +QString WindowsCommons::getProgramFilesPath() { + wchar_t* path = nullptr; + + if (SUCCEEDED( + SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &path))) { + auto guard = qScopeGuard([&] { CoTaskMemFree(path); }); + return QString::fromWCharArray(path); + } + return QString(); +} + +// static +QString WindowsCommons::getTunnelLogFilePath() { + // Return WireGuard's log file path, "\Program Files\WireGuard\Data\log.bin", + // if the directory path exists + auto programFilesPath = getProgramFilesPath(); + if (!programFilesPath.isEmpty()) { + QDir programFilesDir(programFilesPath); + + if (programFilesDir.exists()) { + QDir wireGuardDir(programFilesDir.filePath(WIREGUARD_DIR)); + + if (wireGuardDir.exists()) { + QDir wireGuardDataDir(wireGuardDir.filePath(DATA_DIR)); + + if (wireGuardDataDir.exists()) { + return wireGuardDataDir.filePath("log.bin"); + } + } } - - QDir vpnDir(dir.filePath(VPN_NAME)); - if (!vpnDir.exists()) { - continue; - } - - return vpnDir.filePath("log.bin"); } + logger.debug() << "Failed to find WireGuard Tunnel log file"; return QString(); } +// static +int WindowsCommons::AdapterIndexTo(const QHostAddress& dst) { + logger.debug() << "Getting Current Internet Adapter that routes to" + << logger.sensitive(dst.toString()); + quint32_be ipBigEndian; + quint32 ip = dst.toIPv4Address(); + qToBigEndian(ip, &ipBigEndian); + _MIB_IPFORWARDROW routeInfo; + auto result = GetBestRoute(ipBigEndian, 0, &routeInfo); + if (result != NO_ERROR) { + return -1; + } + auto adapter = + QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex); + logger.debug() << "Internet Adapter:" << adapter.name(); + return routeInfo.dwForwardIfIndex; +} + // static int WindowsCommons::VPNAdapterIndex() { // For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:( @@ -102,7 +143,7 @@ int WindowsCommons::VPNAdapterIndex() { // Static QString WindowsCommons::getCurrentPath() { - QByteArray buffer(2048, 0xFF); + QByteArray buffer(2048, 0xFFu); auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size()); if (ok == ERROR_INSUFFICIENT_BUFFER) { diff --git a/client/platforms/windows/windowscommons.h b/client/platforms/windows/windowscommons.h index eb3e675c..ec3b779e 100644 --- a/client/platforms/windows/windowscommons.h +++ b/client/platforms/windows/windowscommons.h @@ -19,9 +19,14 @@ class WindowsCommons final { // Returns the Interface Index of the VPN Adapter static int VPNAdapterIndex(); - + // Returns the Interface Index that could Route to dst + static int AdapterIndexTo(const QHostAddress& dst); // Returns the Path of the Current process static QString getCurrentPath(); + + private: + static QString getTunnelLogFilePath(); + static QString getProgramFilesPath(); }; #endif // WINDOWSCOMMONS_H diff --git a/client/platforms/windows/windowsnetworkwatcher.cpp b/client/platforms/windows/windowsnetworkwatcher.cpp index 2de5a726..7b1096b0 100644 --- a/client/platforms/windows/windowsnetworkwatcher.cpp +++ b/client/platforms/windows/windowsnetworkwatcher.cpp @@ -4,6 +4,7 @@ #include "windowsnetworkwatcher.h" +#include #include #include "leakdetector.h" @@ -136,9 +137,4 @@ void WindowsNetworkWatcher::processWlan(PWLAN_NOTIFICATION_DATA data) { logger.debug() << "Unsecure network:" << logger.sensitive(ssid) << "id:" << logger.sensitive(bssid); emit unsecuredNetwork(ssid, bssid); -} - -NetworkWatcherImpl::TransportType WindowsNetworkWatcher::getTransportType() { - // TODO: Implement this once we update to Qt6.3 (VPN-3511) - return TransportType_Other; -} +} \ No newline at end of file diff --git a/client/platforms/windows/windowsnetworkwatcher.h b/client/platforms/windows/windowsnetworkwatcher.h index 29b99808..1b153746 100644 --- a/client/platforms/windows/windowsnetworkwatcher.h +++ b/client/platforms/windows/windowsnetworkwatcher.h @@ -17,8 +17,6 @@ class WindowsNetworkWatcher final : public NetworkWatcherImpl { void initialize() override; - NetworkWatcherImpl::TransportType getTransportType() override; - private: static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context); diff --git a/client/platforms/windows/windowspingsender.cpp b/client/platforms/windows/windowspingsender.cpp index ad78ad31..8b07a80f 100644 --- a/client/platforms/windows/windowspingsender.cpp +++ b/client/platforms/windows/windowspingsender.cpp @@ -7,6 +7,8 @@ #include #include #include +#include + // Note: This important must come after the previous three. // clang-format off #include @@ -16,17 +18,58 @@ #include "leakdetector.h" #include "logger.h" -#include "windowscommons.h" #include "platforms/windows/windowsutils.h" +#include "windowscommons.h" #pragma comment(lib, "Ws2_32") +/* + * On 64 Bit systems we need to use another struct. + */ +#ifdef _WIN64 +using MZ_ICMP_ECHO_REPLY = ICMP_ECHO_REPLY32; +#else +using MZ_ICMP_ECHO_REPLY = ICMP_ECHO_REPLY; +#endif + constexpr WORD WindowsPingPayloadSize = sizeof(quint16); +constexpr size_t ICMP_ERR_SIZE = 8; +/* + * IcmpSendEcho2 expects us to provide a Buffer that is + * at least this size + */ +constexpr size_t MinimumReplyBufferSize = + sizeof(ICMP_ECHO_REPLY) + WindowsPingPayloadSize + ICMP_ERR_SIZE + + sizeof(IO_STATUS_BLOCK); +/** + * ICMP_ECHO_REPLY32 is smaller than ICMP_ECHO_REPLY, so if we use that due to + * binary compat Windows will add some padding. + */ +constexpr auto reply_padding = + sizeof(ICMP_ECHO_REPLY) - sizeof(MZ_ICMP_ECHO_REPLY); + +// Disable Packing, so the compiler does not add padding in this struct between +// different sized types. +#pragma pack(push, 1) +struct ICMP_ECHO_REPLY_BUFFER { + MZ_ICMP_ECHO_REPLY reply; + std::array padding; + quint16 payload; + std::array icmp_error; + IO_STATUS_BLOCK status; +}; +#pragma pack(pop) + +// If the Size is not the MinimumReplyBufferSize, the compiler added +// padding, so the fields will not be properly aligned with +// what IcmpSendEcho2 will write. +static_assert(sizeof(ICMP_ECHO_REPLY_BUFFER) == MinimumReplyBufferSize, + "Fulfills the size requirements"); struct WindowsPingSenderPrivate { HANDLE m_handle; HANDLE m_event; - unsigned char m_buffer[sizeof(ICMP_ECHO_REPLY) + WindowsPingPayloadSize + 8]; + ICMP_ECHO_REPLY_BUFFER m_replyBuffer; }; namespace { @@ -58,7 +101,7 @@ WindowsPingSender::WindowsPingSender(const QHostAddress& source, QObject::connect(m_notifier, &QWinEventNotifier::activated, this, &WindowsPingSender::pingEventReady); - memset(m_private->m_buffer, 0, sizeof(m_private->m_buffer)); + m_private->m_replyBuffer = {}; } WindowsPingSender::~WindowsPingSender() { @@ -86,16 +129,33 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { quint32 v4dst = dest.toIPv4Address(); if (m_source.isNull()) { - IcmpSendEcho2(m_private->m_handle, m_private->m_event, nullptr, nullptr, - qToBigEndian(v4dst), &sequence, sizeof(sequence), - nullptr, m_private->m_buffer, sizeof(m_private->m_buffer), - 10000); + IcmpSendEcho2(m_private->m_handle, // IcmpHandle, + m_private->m_event, // Event + nullptr, // ApcRoutine + nullptr, // ApcContext + qToBigEndian(v4dst), // DestinationAddress + &sequence, // RequestData + sizeof(sequence), // RequestSize + nullptr, // RequestOptions + &m_private->m_replyBuffer, // [OUT] ReplyBuffer + sizeof(m_private->m_replyBuffer), // ReplySize + 10000 // Timeout + ); } else { quint32 v4src = m_source.toIPv4Address(); - IcmpSendEcho2Ex(m_private->m_handle, m_private->m_event, nullptr, nullptr, - qToBigEndian(v4src), qToBigEndian(v4dst), - &sequence, sizeof(sequence), nullptr, m_private->m_buffer, - sizeof(m_private->m_buffer), 10000); + IcmpSendEcho2Ex(m_private->m_handle, // IcmpHandle + m_private->m_event, // Event + nullptr, // ApcRoutine + nullptr, // ApcContext + qToBigEndian(v4src), // SourceAddress + qToBigEndian(v4dst), // DestinationAddress + &sequence, // RequestData + sizeof(sequence), // RequestSize + nullptr, // RequestOptions + &m_private->m_replyBuffer, // [OUT] ReplyBuffer + sizeof(m_private->m_replyBuffer), // ReplySize + 10000 // Timeout + ); } DWORD status = GetLastError(); @@ -108,8 +168,11 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) { } void WindowsPingSender::pingEventReady() { - DWORD replyCount = - IcmpParseReplies(m_private->m_buffer, sizeof(m_private->m_buffer)); + // Cleanup all data once we're done with m_replyBuffer. + const auto guard = qScopeGuard([this]() { m_private->m_replyBuffer = {}; }); + + DWORD replyCount = IcmpParseReplies(&m_private->m_replyBuffer, + sizeof(m_private->m_replyBuffer)); if (replyCount == 0) { DWORD error = GetLastError(); if (error == IP_REQ_TIMED_OUT) { @@ -120,14 +183,25 @@ void WindowsPingSender::pingEventReady() { << " Message: " << errmsg; return; } - - const ICMP_ECHO_REPLY* replies = (const ICMP_ECHO_REPLY*)m_private->m_buffer; - for (DWORD i = 0; i < replyCount; i++) { - if (replies[i].DataSize < sizeof(quint16)) { - continue; - } - quint16 sequence; - memcpy(&sequence, replies[i].Data, sizeof(quint16)); - emit recvPing(sequence); + // We only allocated for one reply, so more should be impossible. + if (replyCount != 1) { + logger.error() << "Invalid amount of responses recieved"; + return; } + if (m_private->m_replyBuffer.reply.Data == nullptr) { + logger.error() << "Did get a ping response without payload"; + return; + } + // Assert that the (void*) pointer of Data is pointing + // to our ReplyBuffer payload. + if (m_private->m_replyBuffer.reply.Data == nullptr) { + logger.error() << "Did get a ping response without payload"; + return; + } + // Assert that the (void*) pointer of Data is pointing + // to our ReplyBuffer payload. + assert(m_private->m_replyBuffer.reply.Data == + static_cast(&m_private->m_replyBuffer.payload)); + + emit recvPing(m_private->m_replyBuffer.payload); }