parent
24759c92ad
commit
5bd8c33a6d
22 changed files with 319 additions and 172 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QNetworkInformation>
|
||||||
|
|
||||||
|
|
||||||
class NetworkWatcherImpl;
|
class NetworkWatcherImpl;
|
||||||
|
|
||||||
|
|
@ -16,26 +17,27 @@ 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:
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
bool m_reportUnsecuredNetwork = false;
|
bool m_reportUnsecuredNetwork = false;
|
||||||
|
|
||||||
|
|
@ -46,6 +48,9 @@ class NetworkWatcher final : public QObject {
|
||||||
|
|
||||||
// This is used to connect NotificationHandler lazily.
|
// This is used to connect NotificationHandler lazily.
|
||||||
bool m_firstNotification = true;
|
bool m_firstNotification = true;
|
||||||
|
|
||||||
|
// Used to simulate network disconnection in the Inspector
|
||||||
|
bool m_simulatedDisconnection = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NETWORKWATCHER_H
|
#endif // NETWORKWATCHER_H
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
#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;
|
||||||
|
|
@ -33,10 +34,7 @@ class NetworkWatcherImpl : public QObject {
|
||||||
};
|
};
|
||||||
Q_ENUM(TransportType);
|
Q_ENUM(TransportType);
|
||||||
|
|
||||||
// Returns the current type of Network Connection
|
signals:
|
||||||
virtual TransportType getTransportType() = 0;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
// Fires when the Device Connects to an unsecured Network
|
// Fires when the Device Connects to an unsecured Network
|
||||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||||
// Fires on when the connected WIFI Changes
|
// Fires on when the connected WIFI Changes
|
||||||
|
|
@ -44,10 +42,7 @@ class NetworkWatcherImpl : public QObject {
|
||||||
// too.
|
// too.
|
||||||
void networkChanged(QString newBSSID);
|
void networkChanged(QString newBSSID);
|
||||||
|
|
||||||
// Fired when the Device changed the Type of Transport
|
private:
|
||||||
void transportChanged(NetworkWatcherImpl::TransportType transportType);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
5
client/platforms/linux/linuxnetworkwatcher.h
Normal file → Executable 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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,32 @@ void MacOSUtils::enableLoginItem(bool startAtBoot) {
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
// 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);
|
Boolean ok = SMLoginItemSetEnabled(cfs, startAtBoot ? YES : NO);
|
||||||
logger.debug() << "Result: " << ok;
|
logger.debug() << "Result: " << ok;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir vpnDir(dir.filePath(VPN_NAME));
|
|
||||||
if (!vpnDir.exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vpnDir.filePath("log.bin");
|
|
||||||
}
|
|
||||||
|
|
||||||
return QString();
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
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<PVOID>(&m_private->m_replyBuffer.payload));
|
||||||
|
|
||||||
const ICMP_ECHO_REPLY* replies = (const ICMP_ECHO_REPLY*)m_private->m_buffer;
|
emit recvPing(m_private->m_replyBuffer.payload);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue