Update Mozilla upstream (#790)

* Update Mozilla upstream
This commit is contained in:
Mykola Baibuz 2024-05-08 22:02:02 +01:00 committed by GitHub
parent 24759c92ad
commit 5bd8c33a6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 319 additions and 172 deletions

View file

@ -171,9 +171,15 @@ void NetworkWatcher::unsecuredNetwork(const QString& networkName,
} }
QString NetworkWatcher::getCurrentTransport() { QNetworkInformation::Reachability NetworkWatcher::getReachability() {
auto type = m_impl->getTransportType(); if (m_simulatedDisconnection) {
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>(); return QNetworkInformation::Reachability::Disconnected;
return QString(metaEnum.valueToKey(type)) } else if (QNetworkInformation::instance()) {
.remove("TransportType_", Qt::CaseSensitive); return QNetworkInformation::instance()->reachability();
}
return QNetworkInformation::Reachability::Unknown;
}
void NetworkWatcher::simulateDisconnection(bool simulatedDisconnection) {
m_simulatedDisconnection = simulatedDisconnection;
} }

View file

@ -7,45 +7,50 @@
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QMap> #include <QMap>
#include <QObject> #include <QNetworkInformation>
class NetworkWatcherImpl; class NetworkWatcherImpl;
// This class watches for network changes to detect unsecured wifi. // This class watches for network changes to detect unsecured wifi.
class NetworkWatcher final : public QObject { class NetworkWatcher final : public QObject {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(NetworkWatcher) Q_DISABLE_COPY_MOVE(NetworkWatcher)
public: public:
NetworkWatcher(); NetworkWatcher();
~NetworkWatcher(); ~NetworkWatcher();
void initialize(); void initialize();
// public for the inspector. // Public for the Inspector.
void unsecuredNetwork(const QString& networkName, const QString& networkId); 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: signals:
void networkChange(); void networkChange();
private: private:
void settingsChanged(); void settingsChanged();
// void notificationClicked(NotificationHandler::Message message); private:
bool m_active = false;
bool m_reportUnsecuredNetwork = false;
private: // Platform-specific implementation.
bool m_active = false; NetworkWatcherImpl* m_impl = nullptr;
bool m_reportUnsecuredNetwork = false;
// Platform-specific implementation. QMap<QString, QElapsedTimer> m_networks;
NetworkWatcherImpl* m_impl = nullptr;
QMap<QString, QElapsedTimer> m_networks; // This is used to connect NotificationHandler lazily.
bool m_firstNotification = true;
// This is used to connect NotificationHandler lazily. // Used to simulate network disconnection in the Inspector
bool m_firstNotification = true; bool m_simulatedDisconnection = false;
}; };
#endif // NETWORKWATCHER_H #endif // NETWORKWATCHER_H

View file

@ -5,50 +5,45 @@
#ifndef NETWORKWATCHERIMPL_H #ifndef NETWORKWATCHERIMPL_H
#define NETWORKWATCHERIMPL_H #define NETWORKWATCHERIMPL_H
#include <QNetworkInformation>
#include <QObject> #include <QObject>
class NetworkWatcherImpl : public QObject { class NetworkWatcherImpl : public QObject {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl) Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
public: public:
NetworkWatcherImpl(QObject* parent) : QObject(parent) {} 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 start() { m_active = true; }
virtual void stop() { m_active = false; } virtual void stop() { m_active = false; }
bool isActive() const { return m_active; } bool isActive() const { return m_active; }
enum TransportType { enum TransportType {
TransportType_Unknown = 0, TransportType_Unknown = 0,
TransportType_Ethernet = 1, TransportType_Ethernet = 1,
TransportType_WiFi = 2, TransportType_WiFi = 2,
TransportType_Cellular = 3, // In Case the API does not retun the gsm type TransportType_Cellular = 3, // In Case the API does not retun the gsm type
TransportType_Other = 4, // I.e USB thethering TransportType_Other = 4, // I.e USB thethering
TransportType_None = 5 // I.e Airplane Mode or no active network device TransportType_None = 5 // I.e Airplane Mode or no active network device
}; };
Q_ENUM(TransportType); Q_ENUM(TransportType);
// Returns the current type of Network Connection signals:
virtual TransportType getTransportType() = 0; // 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: private:
// Fires when the Device Connects to an unsecured Network bool m_active = false;
void unsecuredNetwork(const QString& networkName, const QString& networkId);
// Fires on when the connected WIFI Changes
// TODO: Only windows-networkwatcher has this, the other plattforms should
// too.
void networkChanged(QString newBSSID);
// Fired when the Device changed the Type of Transport
void transportChanged(NetworkWatcherImpl::TransportType transportType);
private:
bool m_active = false;
}; };
#endif // NETWORKWATCHERIMPL_H #endif // NETWORKWATCHERIMPL_H

View file

@ -8,11 +8,11 @@
DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent) DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent)
: NetworkWatcherImpl(parent) { : NetworkWatcherImpl(parent) {
MZ_COUNT_CTOR(DummyNetworkWatcher); MZ_COUNT_CTOR(DummyNetworkWatcher);
} }
DummyNetworkWatcher::~DummyNetworkWatcher() { DummyNetworkWatcher::~DummyNetworkWatcher() {
MZ_COUNT_DTOR(DummyNetworkWatcher); MZ_COUNT_DTOR(DummyNetworkWatcher);
} }
void DummyNetworkWatcher::initialize() {} void DummyNetworkWatcher::initialize() {}

View file

@ -8,15 +8,11 @@
#include "networkwatcherimpl.h" #include "networkwatcherimpl.h"
class DummyNetworkWatcher final : public NetworkWatcherImpl { class DummyNetworkWatcher final : public NetworkWatcherImpl {
public: public:
DummyNetworkWatcher(QObject* parent); DummyNetworkWatcher(QObject* parent);
~DummyNetworkWatcher(); ~DummyNetworkWatcher();
void initialize() override; void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override {
return TransportType_Other;
};
}; };
#endif // DUMMYNETWORKWATCHER_H #endif // DUMMYNETWORKWATCHER_H

View file

@ -15,7 +15,6 @@ class IOSNetworkWatcher : public NetworkWatcherImpl {
~IOSNetworkWatcher(); ~IOSNetworkWatcher();
void initialize() override; void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override;
private: private:
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path); NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);

View file

@ -37,16 +37,6 @@ void IOSNetworkWatcher::initialize() {
//TODO IMPL FOR AMNEZIA //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) { NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) {
if (path == nil) { if (path == nil) {
return NetworkWatcherImpl::TransportType_Unknown; return NetworkWatcherImpl::TransportType_Unknown;

5
client/platforms/linux/linuxnetworkwatcher.h Normal file → Executable file
View file

@ -22,11 +22,6 @@ class LinuxNetworkWatcher final : public NetworkWatcherImpl {
void start() override; void start() override;
NetworkWatcherImpl::TransportType getTransportType() {
// TODO: Find out how to do that on linux generally. (VPN-2382)
return NetworkWatcherImpl::TransportType_Unknown;
};
signals: signals:
void checkDevicesInThread(); void checkDevicesInThread();

View file

@ -95,6 +95,11 @@ void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
return; 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. // Check for a default route, which should have a netmask of zero.
const struct sockaddr* sa = const struct sockaddr* sa =
@ -156,6 +161,11 @@ void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) { !(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
return; 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. // Ignore route changes that we caused, or routes on the tunnel interface.
if (rtm->rtm_index == m_ifindex) { if (rtm->rtm_index == m_ifindex) {
return; return;

View file

@ -83,7 +83,7 @@ void MacOSPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
packet.icmp_seq = htons(sequence); packet.icmp_seq = htons(sequence);
packet.icmp_cksum = inetChecksum(&packet, sizeof(packet)); 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)) { (struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) {
logger.error() << "ping sending failed:" << strerror(errno); logger.error() << "ping sending failed:" << strerror(errno);
emit criticalPingError(); emit criticalPingError();
@ -107,9 +107,9 @@ void MacOSPingSender::socketReady() {
iov.iov_base = packet; iov.iov_base = packet;
iov.iov_len = IP_MAXPACKET; 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) { if (rc <= 0) {
logger.error() << "Recvmsg failed"; logger.error() << "Recvmsg failed:" << strerror(errno);
return; return;
} }

View file

@ -41,11 +41,33 @@ void MacOSUtils::enableLoginItem(bool startAtBoot) {
Q_ASSERT(appId); Q_ASSERT(appId);
NSString* loginItemAppId = NSString* loginItemAppId =
QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString(); QString("%1.login-item").arg(QString::fromNSString(appId)).toNSString();
CFStringRef cfs = (__bridge CFStringRef)loginItemAppId;
Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO); // For macOS 13 and beyond, register() and unregister() methods
logger.debug() << "Result: " << ok; // 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 { namespace {

View file

@ -4,8 +4,11 @@
#include "dnsutilswindows.h" #include "dnsutilswindows.h"
#include <WS2tcpip.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <windows.h> #include <windows.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <QProcess> #include <QProcess>
#include <QTextStream> #include <QTextStream>
@ -39,30 +42,27 @@ DnsUtilsWindows::~DnsUtilsWindows() {
bool DnsUtilsWindows::updateResolvers(const QString& ifname, bool DnsUtilsWindows::updateResolvers(const QString& ifname,
const QList<QHostAddress>& resolvers) { const QList<QHostAddress>& resolvers) {
NET_LUID luid; MIB_IF_ROW2 entry;
if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(), &luid) != 0) { if (ConvertInterfaceAliasToLuid((wchar_t*)ifname.utf16(),
&entry.InterfaceLuid) != 0) {
logger.error() << "Failed to resolve LUID for" << ifname; logger.error() << "Failed to resolve LUID for" << ifname;
return false; 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; logger.debug() << "Configuring DNS for" << ifname;
if (m_setInterfaceDnsSettingsProcAddr == nullptr) { if (m_setInterfaceDnsSettingsProcAddr == nullptr) {
return updateResolversNetsh(resolvers); return updateResolversNetsh(entry.InterfaceIndex, resolvers);
} }
return updateResolversWin32(resolvers); return updateResolversWin32(entry.InterfaceGuid, resolvers);
} }
bool DnsUtilsWindows::updateResolversWin32( bool DnsUtilsWindows::updateResolversWin32(
const QList<QHostAddress>& resolvers) { GUID guid, const QList<QHostAddress>& resolvers) {
GUID guid;
NET_LUID luid;
luid.Value = m_luid;
if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
logger.error() << "Failed to resolve GUID";
return false;
}
QStringList v4resolvers; QStringList v4resolvers;
QStringList v6resolvers; QStringList v6resolvers;
for (const QHostAddress& addr : resolvers) { 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"; "interface %1 add dnsservers name=%2 address=%3 validate=no\r\n";
bool DnsUtilsWindows::updateResolversNetsh( bool DnsUtilsWindows::updateResolversNetsh(
const QList<QHostAddress>& resolvers) { int ifindex, const QList<QHostAddress>& resolvers) {
QProcess netsh; 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.setProgram("netsh");
netsh.start(); netsh.start();
if (!netsh.waitForStarted(WINDOWS_NETSH_TIMEOUT_MSEC)) { if (!netsh.waitForStarted(WINDOWS_NETSH_TIMEOUT_MSEC)) {
@ -166,12 +158,26 @@ bool DnsUtilsWindows::updateResolversNetsh(
bool DnsUtilsWindows::restoreResolvers() { bool DnsUtilsWindows::restoreResolvers() {
if (m_luid == 0) { if (m_luid == 0) {
// If the DNS hasn't been configured, there is nothing to restore.
return true; 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<QHostAddress> empty; QList<QHostAddress> empty;
if (m_setInterfaceDnsSettingsProcAddr == nullptr) { if (m_setInterfaceDnsSettingsProcAddr == nullptr) {
return updateResolversNetsh(empty); return updateResolversNetsh(entry.InterfaceIndex, empty);
} }
return updateResolversWin32(empty); return updateResolversWin32(entry.InterfaceGuid, empty);
} }

View file

@ -27,8 +27,8 @@ class DnsUtilsWindows final : public DnsUtils {
quint64 m_luid = 0; quint64 m_luid = 0;
DWORD (*m_setInterfaceDnsSettingsProcAddr)(GUID, const void*) = nullptr; DWORD (*m_setInterfaceDnsSettingsProcAddr)(GUID, const void*) = nullptr;
bool updateResolversWin32(const QList<QHostAddress>& resolvers); bool updateResolversWin32(GUID, const QList<QHostAddress>& resolvers);
bool updateResolversNetsh(const QList<QHostAddress>& resolvers); bool updateResolversNetsh(int ifindex, const QList<QHostAddress>& resolvers);
}; };
#endif // DNSUTILSWINDOWS_H #endif // DNSUTILSWINDOWS_H

View file

@ -38,7 +38,6 @@ class WindowsDaemon final : public Daemon {
Inactive, Inactive,
}; };
State m_state = Inactive;
int m_inetAdapterIndex = -1; int m_inetAdapterIndex = -1;
WireguardUtilsWindows* m_wgutils = nullptr; WireguardUtilsWindows* m_wgutils = nullptr;

View file

@ -114,7 +114,7 @@ void WindowsRouteMonitor::updateValidInterfaces(int family) {
void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data, void WindowsRouteMonitor::updateExclusionRoute(MIB_IPFORWARD_ROW2* data,
void* ptable) { void* ptable) {
PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable); PMIB_IPFORWARD_TABLE2 table = reinterpret_cast<PMIB_IPFORWARD_TABLE2>(ptable);
SOCKADDR_INET nexthop = {0}; SOCKADDR_INET nexthop = {};
quint64 bestLuid = 0; quint64 bestLuid = 0;
int bestMatch = -1; int bestMatch = -1;
ULONG bestMetric = ULONG_MAX; ULONG bestMetric = ULONG_MAX;

View file

@ -39,7 +39,7 @@ Logger logger("tunnel.dll");
WindowsTunnelLogger::WindowsTunnelLogger(const QString& filename, WindowsTunnelLogger::WindowsTunnelLogger(const QString& filename,
QObject* parent) QObject* parent)
: QObject(parent), m_logfile(filename, this), m_timer(this) { : QObject(parent), m_timer(this), m_logfile(filename, this) {
MZ_COUNT_CTOR(WindowsTunnelLogger); MZ_COUNT_CTOR(WindowsTunnelLogger);
m_startTime = QDateTime::currentMSecsSinceEpoch() * 1000000; m_startTime = QDateTime::currentMSecsSinceEpoch() * 1000000;

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WindowsTunnelService.h" #include "windowstunnelservice.h"
#include <Windows.h> #include <Windows.h>
@ -30,12 +30,22 @@ static bool waitForServiceStatus(SC_HANDLE service, DWORD expectedStatus);
WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) { WindowsTunnelService::WindowsTunnelService(QObject* parent) : QObject(parent) {
MZ_COUNT_CTOR(WindowsTunnelService); MZ_COUNT_CTOR(WindowsTunnelService);
logger.debug() << "WindowsTunnelService created.";
m_scm = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); m_scm = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
if (m_scm == nullptr) { if (m_scm == nullptr) {
WindowsUtils::windowsLog("Failed to open SCManager"); 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); connect(&m_timer, &QTimer::timeout, this, &WindowsTunnelService::timeout);
} }

View file

@ -8,6 +8,7 @@
#include <d3d11.h> #include <d3d11.h>
#include <dxgi.h> #include <dxgi.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <shlobj_core.h>
#include <QDir> #include <QDir>
#include <QHostAddress> #include <QHostAddress>
@ -19,9 +20,9 @@
#include "logger.h" #include "logger.h"
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
#define TUNNEL_SERVICE_NAME L"WireGuardTunnel$amnvpn"
constexpr const char* VPN_NAME = "AmneziaVPN"; constexpr const char* VPN_NAME = "AmneziaVPN";
constexpr const char* WIREGUARD_DIR = "WireGuard";
constexpr const char* DATA_DIR = "Data";
namespace { namespace {
Logger logger("WindowsCommons"); Logger logger("WindowsCommons");
@ -67,27 +68,67 @@ QString WindowsCommons::tunnelConfigFile() {
return QString(); return QString();
} }
// static
QString WindowsCommons::tunnelLogFile() { QString WindowsCommons::tunnelLogFile() {
QStringList paths = static QString tunnelLogFilePath = getTunnelLogFilePath();
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); return tunnelLogFilePath;
}
for (const QString& path : paths) { // static
QDir dir(path); QString WindowsCommons::getProgramFilesPath() {
if (!dir.exists()) { wchar_t* path = nullptr;
continue;
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(); 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 // static
int WindowsCommons::VPNAdapterIndex() { int WindowsCommons::VPNAdapterIndex() {
// For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:( // For someReason QNetworkInterface::fromName(MozillaVPN) does not work >:(
@ -102,7 +143,7 @@ int WindowsCommons::VPNAdapterIndex() {
// Static // Static
QString WindowsCommons::getCurrentPath() { QString WindowsCommons::getCurrentPath() {
QByteArray buffer(2048, 0xFF); QByteArray buffer(2048, 0xFFu);
auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size()); auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size());
if (ok == ERROR_INSUFFICIENT_BUFFER) { if (ok == ERROR_INSUFFICIENT_BUFFER) {

View file

@ -19,9 +19,14 @@ class WindowsCommons final {
// Returns the Interface Index of the VPN Adapter // Returns the Interface Index of the VPN Adapter
static int VPNAdapterIndex(); 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 // Returns the Path of the Current process
static QString getCurrentPath(); static QString getCurrentPath();
private:
static QString getTunnelLogFilePath();
static QString getProgramFilesPath();
}; };
#endif // WINDOWSCOMMONS_H #endif // WINDOWSCOMMONS_H

View file

@ -4,6 +4,7 @@
#include "windowsnetworkwatcher.h" #include "windowsnetworkwatcher.h"
#include <QNetworkInformation>
#include <QScopeGuard> #include <QScopeGuard>
#include "leakdetector.h" #include "leakdetector.h"
@ -137,8 +138,3 @@ void WindowsNetworkWatcher::processWlan(PWLAN_NOTIFICATION_DATA data) {
<< "id:" << logger.sensitive(bssid); << "id:" << logger.sensitive(bssid);
emit unsecuredNetwork(ssid, bssid); emit unsecuredNetwork(ssid, bssid);
} }
NetworkWatcherImpl::TransportType WindowsNetworkWatcher::getTransportType() {
// TODO: Implement this once we update to Qt6.3 (VPN-3511)
return TransportType_Other;
}

View file

@ -17,8 +17,6 @@ class WindowsNetworkWatcher final : public NetworkWatcherImpl {
void initialize() override; void initialize() override;
NetworkWatcherImpl::TransportType getTransportType() override;
private: private:
static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context); static void wlanCallback(PWLAN_NOTIFICATION_DATA data, PVOID context);

View file

@ -7,6 +7,8 @@
#include <WS2tcpip.h> #include <WS2tcpip.h>
#include <Windows.h> #include <Windows.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <winternl.h>
// Note: This important must come after the previous three. // Note: This important must come after the previous three.
// clang-format off // clang-format off
#include <IcmpAPI.h> #include <IcmpAPI.h>
@ -16,17 +18,58 @@
#include "leakdetector.h" #include "leakdetector.h"
#include "logger.h" #include "logger.h"
#include "windowscommons.h"
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
#include "windowscommons.h"
#pragma comment(lib, "Ws2_32") #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 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<uint8_t, reply_padding> padding;
quint16 payload;
std::array<char8_t, ICMP_ERR_SIZE> 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 { struct WindowsPingSenderPrivate {
HANDLE m_handle; HANDLE m_handle;
HANDLE m_event; HANDLE m_event;
unsigned char m_buffer[sizeof(ICMP_ECHO_REPLY) + WindowsPingPayloadSize + 8]; ICMP_ECHO_REPLY_BUFFER m_replyBuffer;
}; };
namespace { namespace {
@ -58,7 +101,7 @@ WindowsPingSender::WindowsPingSender(const QHostAddress& source,
QObject::connect(m_notifier, &QWinEventNotifier::activated, this, QObject::connect(m_notifier, &QWinEventNotifier::activated, this,
&WindowsPingSender::pingEventReady); &WindowsPingSender::pingEventReady);
memset(m_private->m_buffer, 0, sizeof(m_private->m_buffer)); m_private->m_replyBuffer = {};
} }
WindowsPingSender::~WindowsPingSender() { WindowsPingSender::~WindowsPingSender() {
@ -86,16 +129,33 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
quint32 v4dst = dest.toIPv4Address(); quint32 v4dst = dest.toIPv4Address();
if (m_source.isNull()) { if (m_source.isNull()) {
IcmpSendEcho2(m_private->m_handle, m_private->m_event, nullptr, nullptr, IcmpSendEcho2(m_private->m_handle, // IcmpHandle,
qToBigEndian<quint32>(v4dst), &sequence, sizeof(sequence), m_private->m_event, // Event
nullptr, m_private->m_buffer, sizeof(m_private->m_buffer), nullptr, // ApcRoutine
10000); nullptr, // ApcContext
qToBigEndian<quint32>(v4dst), // DestinationAddress
&sequence, // RequestData
sizeof(sequence), // RequestSize
nullptr, // RequestOptions
&m_private->m_replyBuffer, // [OUT] ReplyBuffer
sizeof(m_private->m_replyBuffer), // ReplySize
10000 // Timeout
);
} else { } else {
quint32 v4src = m_source.toIPv4Address(); quint32 v4src = m_source.toIPv4Address();
IcmpSendEcho2Ex(m_private->m_handle, m_private->m_event, nullptr, nullptr, IcmpSendEcho2Ex(m_private->m_handle, // IcmpHandle
qToBigEndian<quint32>(v4src), qToBigEndian<quint32>(v4dst), m_private->m_event, // Event
&sequence, sizeof(sequence), nullptr, m_private->m_buffer, nullptr, // ApcRoutine
sizeof(m_private->m_buffer), 10000); nullptr, // ApcContext
qToBigEndian<quint32>(v4src), // SourceAddress
qToBigEndian<quint32>(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(); DWORD status = GetLastError();
@ -108,8 +168,11 @@ void WindowsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
} }
void WindowsPingSender::pingEventReady() { void WindowsPingSender::pingEventReady() {
DWORD replyCount = // Cleanup all data once we're done with m_replyBuffer.
IcmpParseReplies(m_private->m_buffer, sizeof(m_private->m_buffer)); const auto guard = qScopeGuard([this]() { m_private->m_replyBuffer = {}; });
DWORD replyCount = IcmpParseReplies(&m_private->m_replyBuffer,
sizeof(m_private->m_replyBuffer));
if (replyCount == 0) { if (replyCount == 0) {
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == IP_REQ_TIMED_OUT) { if (error == IP_REQ_TIMED_OUT) {
@ -120,14 +183,25 @@ void WindowsPingSender::pingEventReady() {
<< " Message: " << errmsg; << " Message: " << errmsg;
return; return;
} }
// We only allocated for one reply, so more should be impossible.
const ICMP_ECHO_REPLY* replies = (const ICMP_ECHO_REPLY*)m_private->m_buffer; if (replyCount != 1) {
for (DWORD i = 0; i < replyCount; i++) { logger.error() << "Invalid amount of responses recieved";
if (replies[i].DataSize < sizeof(quint16)) { return;
continue;
}
quint16 sequence;
memcpy(&sequence, replies[i].Data, sizeof(quint16));
emit recvPing(sequence);
} }
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<PVOID>(&m_private->m_replyBuffer.payload));
emit recvPing(m_private->m_replyBuffer.payload);
} }