mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-19 02:00:45 +07:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89b94d2588 | |||
| 9166e17a23 | |||
| 37d2b8716d | |||
| 92b168100a | |||
| 0f6db8a238 | |||
| 7c075625d2 | |||
| c6a40b09c9 | |||
| 560c4070b4 | |||
| f6806459fd | |||
| 646b1561f8 | |||
| 6d1e10a2e3 | |||
| af0d561c5c | |||
| 214b18f65f | |||
| bd554fb730 | |||
| 316e64122e |
@@ -292,117 +292,103 @@ QPair<QString, QNetworkInterface> NetworkUtilities::getGatewayAndIface()
|
||||
return { resGateway, QNetworkInterface::interfaceFromIndex(resIndex) };
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
constexpr int BUFFER_SIZE = 100;
|
||||
int received_bytes = 0, msg_len = 0, route_attribute_len = 0;
|
||||
int sock = -1, msgseq = 0;
|
||||
struct nlmsghdr *nlh, *nlmsg;
|
||||
struct rtmsg *route_entry;
|
||||
// This struct contain route attributes (route type)
|
||||
struct rtattr *route_attribute;
|
||||
char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE];
|
||||
char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE];
|
||||
char *ptr = buffer;
|
||||
struct timeval tv;
|
||||
static constexpr size_t BUFFER_SIZE = 8192;
|
||||
|
||||
if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
|
||||
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (sock < 0) {
|
||||
perror("socket failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
memset(msgbuf, 0, sizeof(msgbuf));
|
||||
memset(gateway_address, 0, sizeof(gateway_address));
|
||||
memset(interface, 0, sizeof(interface));
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct timeval tv { 1, 0 };
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
/* point the header and the msg structure pointers into the buffer */
|
||||
nlmsg = (struct nlmsghdr *)msgbuf;
|
||||
struct {
|
||||
struct nlmsghdr hdr;
|
||||
struct rtmsg rt;
|
||||
} req {};
|
||||
req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
req.hdr.nlmsg_type = RTM_GETROUTE;
|
||||
req.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||||
req.hdr.nlmsg_seq = 1;
|
||||
req.hdr.nlmsg_pid = static_cast<uint32_t>(getpid());
|
||||
req.rt.rtm_family = AF_INET;
|
||||
|
||||
/* Fill in the nlmsg header*/
|
||||
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .
|
||||
nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.
|
||||
nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet.
|
||||
nlmsg->nlmsg_pid = getpid(); // PID of process sending the request.
|
||||
|
||||
/* 1 Sec Timeout to avoid stall */
|
||||
tv.tv_sec = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||
/* send msg */
|
||||
if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
|
||||
if (send(sock, &req, req.hdr.nlmsg_len, 0) < 0) {
|
||||
perror("send failed");
|
||||
close(sock);
|
||||
return {};
|
||||
}
|
||||
|
||||
/* receive response */
|
||||
do
|
||||
{
|
||||
received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0);
|
||||
if (received_bytes < 0) {
|
||||
perror("Error in recv");
|
||||
return {};
|
||||
}
|
||||
char buffer[BUFFER_SIZE];
|
||||
size_t total_len = 0;
|
||||
bool done = false;
|
||||
|
||||
nlh = (struct nlmsghdr *) ptr;
|
||||
|
||||
/* Check if the header is valid */
|
||||
if((NLMSG_OK(nlmsg, received_bytes) == 0) ||
|
||||
(nlmsg->nlmsg_type == NLMSG_ERROR))
|
||||
{
|
||||
perror("Error in received packet");
|
||||
return {};
|
||||
}
|
||||
|
||||
/* If we received all data break */
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
while (!done && total_len < BUFFER_SIZE) {
|
||||
ssize_t n = recv(sock, buffer + total_len, BUFFER_SIZE - total_len, 0);
|
||||
if (n <= 0)
|
||||
break;
|
||||
else {
|
||||
ptr += received_bytes;
|
||||
msg_len += received_bytes;
|
||||
}
|
||||
|
||||
/* Break if its not a multi part message */
|
||||
if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0)
|
||||
break;
|
||||
}
|
||||
while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid()));
|
||||
|
||||
/* parse response */
|
||||
for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes))
|
||||
{
|
||||
/* Get the route data */
|
||||
route_entry = (struct rtmsg *) NLMSG_DATA(nlh);
|
||||
|
||||
/* We are just interested in main routing table */
|
||||
if (route_entry->rtm_table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
route_attribute = (struct rtattr *) RTM_RTA(route_entry);
|
||||
route_attribute_len = RTM_PAYLOAD(nlh);
|
||||
|
||||
/* Loop through all attributes */
|
||||
for ( ; RTA_OK(route_attribute, route_attribute_len);
|
||||
route_attribute = RTA_NEXT(route_attribute, route_attribute_len))
|
||||
int scan_len = static_cast<int>(n);
|
||||
for (struct nlmsghdr *h = reinterpret_cast<struct nlmsghdr *>(buffer + total_len);
|
||||
NLMSG_OK(h, scan_len);
|
||||
h = NLMSG_NEXT(h, scan_len))
|
||||
{
|
||||
switch(route_attribute->rta_type) {
|
||||
case RTA_OIF:
|
||||
if_indextoname(*(int *)RTA_DATA(route_attribute), interface);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
inet_ntop(AF_INET, RTA_DATA(route_attribute),
|
||||
gateway_address, sizeof(gateway_address));
|
||||
break;
|
||||
default:
|
||||
if (h->nlmsg_type == NLMSG_DONE || h->nlmsg_type == NLMSG_ERROR) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*gateway_address) && (*interface)) {
|
||||
qDebug() << "Gateway " << gateway_address << " for interface " << interface;
|
||||
break;
|
||||
}
|
||||
total_len += static_cast<size_t>(n);
|
||||
}
|
||||
|
||||
QString resultGw;
|
||||
QString resultIf;
|
||||
int remaining = static_cast<int>(total_len);
|
||||
|
||||
for (struct nlmsghdr *nlh = reinterpret_cast<struct nlmsghdr *>(buffer);
|
||||
NLMSG_OK(nlh, remaining);
|
||||
nlh = NLMSG_NEXT(nlh, remaining))
|
||||
{
|
||||
if (nlh->nlmsg_type == NLMSG_DONE || nlh->nlmsg_type == NLMSG_ERROR)
|
||||
break;
|
||||
if (nlh->nlmsg_type != RTM_NEWROUTE)
|
||||
continue;
|
||||
|
||||
struct rtmsg *rt = static_cast<struct rtmsg *>(NLMSG_DATA(nlh));
|
||||
if (rt->rtm_table != RT_TABLE_MAIN || rt->rtm_family != AF_INET)
|
||||
continue;
|
||||
|
||||
char route_gw[INET_ADDRSTRLEN] = {};
|
||||
char route_if[IF_NAMESIZE] = {};
|
||||
int attr_len = RTM_PAYLOAD(nlh);
|
||||
|
||||
for (struct rtattr *rta = RTM_RTA(rt);
|
||||
RTA_OK(rta, attr_len);
|
||||
rta = RTA_NEXT(rta, attr_len))
|
||||
{
|
||||
if (rta->rta_type == RTA_GATEWAY)
|
||||
inet_ntop(AF_INET, RTA_DATA(rta), route_gw, sizeof(route_gw));
|
||||
else if (rta->rta_type == RTA_OIF)
|
||||
if_indextoname(*static_cast<int *>(RTA_DATA(rta)), route_if);
|
||||
}
|
||||
|
||||
if (!route_gw[0] || !route_if[0])
|
||||
continue;
|
||||
|
||||
QString ifStr(route_if);
|
||||
if (ifStr.startsWith(QLatin1String("amn")) ||
|
||||
ifStr.startsWith(QLatin1String("tun")))
|
||||
continue;
|
||||
|
||||
resultGw = QString::fromLatin1(route_gw);
|
||||
resultIf = QString::fromLatin1(route_if);
|
||||
qDebug() << "Gateway " << route_gw << " for interface " << route_if;
|
||||
break;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
return { gateway_address, QNetworkInterface::interfaceFromName(interface) };
|
||||
return { resultGw, QNetworkInterface::interfaceFromName(resultIf) };
|
||||
#endif
|
||||
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
|
||||
QString gateway;
|
||||
|
||||
@@ -142,10 +142,6 @@ bool Daemon::activate(const InterfaceConfig& config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip)) {
|
||||
@@ -215,6 +211,11 @@ bool Daemon::addExclusionRoute(const IPAddress& prefix) {
|
||||
return true;
|
||||
}
|
||||
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||
if (!m_pendingExclusionRoutes.contains(prefix)) {
|
||||
logger.warning() << "Exclusion route deferred (no gateway):"
|
||||
<< prefix.toString();
|
||||
m_pendingExclusionRoutes.append(prefix);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_excludedAddrSet[prefix] = 1;
|
||||
@@ -480,6 +481,7 @@ bool Daemon::deactivate(bool emitSignals) {
|
||||
wgutils()->deleteExclusionRoute(iterator.key());
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
m_pendingExclusionRoutes.clear();
|
||||
|
||||
m_connections.clear();
|
||||
// Delete the interface
|
||||
@@ -589,6 +591,19 @@ void Daemon::checkHandshake() {
|
||||
|
||||
logger.debug() << "Checking for handshake...";
|
||||
|
||||
if (!m_pendingExclusionRoutes.isEmpty()) {
|
||||
QList<IPAddress> stillPending;
|
||||
for (const IPAddress& prefix : m_pendingExclusionRoutes) {
|
||||
if (!wgutils()->addExclusionRoute(prefix)) {
|
||||
stillPending.append(prefix);
|
||||
} else {
|
||||
logger.debug() << "Deferred exclusion route added:" << prefix.toString();
|
||||
m_excludedAddrSet[prefix] = 1;
|
||||
}
|
||||
}
|
||||
m_pendingExclusionRoutes = stillPending;
|
||||
}
|
||||
|
||||
int pendingHandshakes = 0;
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (ConnectionState& connection : m_connections) {
|
||||
@@ -605,6 +620,7 @@ void Daemon::checkHandshake() {
|
||||
}
|
||||
if (status.m_handshake != 0) {
|
||||
connection.m_date.setMSecsSinceEpoch(status.m_handshake);
|
||||
maybeUpdateResolvers(config);
|
||||
emit connected(status.m_pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ class Daemon : public QObject {
|
||||
};
|
||||
QMap<InterfaceConfig::HopType, ConnectionState> m_connections;
|
||||
QHash<IPAddress, int> m_excludedAddrSet;
|
||||
QList<IPAddress> m_pendingExclusionRoutes;
|
||||
QTimer m_handshakeTimer;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
|
||||
#include "dnsutilslinux.h"
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <QDBusVariant>
|
||||
#include <QtDBus/QtDBus>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const char* DBUS_RESOLVE_SERVICE = "org.freedesktop.resolve1";
|
||||
constexpr const char* DBUS_RESOLVE_PATH = "/org/freedesktop/resolve1";
|
||||
constexpr const char* DBUS_RESOLVE_MANAGER = "org.freedesktop.resolve1.Manager";
|
||||
constexpr const char* DBUS_PROPERTY_INTERFACE =
|
||||
"org.freedesktop.DBus.Properties";
|
||||
|
||||
namespace {
|
||||
Logger logger("DnsUtilsLinux");
|
||||
}
|
||||
@@ -25,188 +19,147 @@ Logger logger("DnsUtilsLinux");
|
||||
DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) {
|
||||
MZ_COUNT_CTOR(DnsUtilsLinux);
|
||||
logger.debug() << "DnsUtilsLinux created.";
|
||||
|
||||
QDBusConnection conn = QDBusConnection::systemBus();
|
||||
m_resolver = new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH,
|
||||
DBUS_RESOLVE_MANAGER, conn, this);
|
||||
}
|
||||
|
||||
DnsUtilsLinux::~DnsUtilsLinux() {
|
||||
MZ_COUNT_DTOR(DnsUtilsLinux);
|
||||
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(iterator.key());
|
||||
argumentList << QVariant::fromValue(iterator.value());
|
||||
m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"),
|
||||
argumentList);
|
||||
}
|
||||
|
||||
if (m_ifindex > 0) {
|
||||
m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex);
|
||||
}
|
||||
|
||||
logger.debug() << "DnsUtilsLinux destroyed.";
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::writeResolvConf(const QList<QHostAddress>& resolvers) {
|
||||
if (resolvers.isEmpty()) return;
|
||||
|
||||
static const QString kPath = QStringLiteral("/etc/resolv.conf");
|
||||
|
||||
if (m_resolvConfOriginal.isEmpty()) {
|
||||
QFileInfo fi(kPath);
|
||||
if (fi.isSymLink()) {
|
||||
m_resolvConfOriginal = fi.symLinkTarget();
|
||||
logger.debug() << "Saved resolv.conf symlink target:"
|
||||
<< m_resolvConfOriginal;
|
||||
if (!m_stateFilePath.isEmpty()) {
|
||||
QFile sf(m_stateFilePath);
|
||||
if (sf.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
sf.write(m_resolvConfOriginal.toUtf8());
|
||||
}
|
||||
} else {
|
||||
QFile orig(kPath);
|
||||
if (orig.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QByteArray content = orig.readAll();
|
||||
orig.close();
|
||||
m_resolvConfOriginal = QStringLiteral("__file__");
|
||||
logger.debug() << "resolv.conf is a regular file; saved content for restore";
|
||||
if (!m_stateFilePath.isEmpty()) {
|
||||
QFile sf(m_stateFilePath);
|
||||
if (sf.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
sf.write("__file__:");
|
||||
sf.write(content);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_resolvConfOriginal = QStringLiteral("__file__");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QFile::remove(kPath);
|
||||
QFile f(kPath);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
logger.warning() << "Failed to write" << kPath << ":" << f.errorString();
|
||||
if (m_resolvConfOriginal != QStringLiteral("__file__"))
|
||||
QFile::link(m_resolvConfOriginal, kPath);
|
||||
m_resolvConfOriginal.clear();
|
||||
if (!m_stateFilePath.isEmpty()) QFile::remove(m_stateFilePath);
|
||||
return;
|
||||
}
|
||||
for (const auto& r : resolvers) {
|
||||
if (r.protocol() == QAbstractSocket::IPv4Protocol)
|
||||
f.write(
|
||||
QStringLiteral("nameserver %1\n").arg(r.toString()).toUtf8());
|
||||
}
|
||||
f.close();
|
||||
logger.debug() << "Wrote resolv.conf with" << resolvers.size()
|
||||
<< "DNS servers";
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::restoreResolvConf() {
|
||||
if (m_resolvConfOriginal.isEmpty()) return;
|
||||
|
||||
const QString original = m_resolvConfOriginal;
|
||||
m_resolvConfOriginal.clear();
|
||||
|
||||
if (!m_stateFilePath.isEmpty())
|
||||
QFile::remove(m_stateFilePath);
|
||||
|
||||
static const char* kPath = "/etc/resolv.conf";
|
||||
|
||||
QFile::remove(kPath);
|
||||
|
||||
if (original == QStringLiteral("__file__")) {
|
||||
if (!m_resolvConfSavedContent.isEmpty()) {
|
||||
QFile f(kPath);
|
||||
if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
f.write(m_resolvConfSavedContent.toUtf8());
|
||||
logger.debug() << "Restored resolv.conf from saved content";
|
||||
}
|
||||
m_resolvConfSavedContent.clear();
|
||||
} else if (QFile::exists(QStringLiteral("/run/systemd/resolve/stub-resolv.conf"))) {
|
||||
QFile::link(QStringLiteral("/run/systemd/resolve/stub-resolv.conf"), kPath);
|
||||
logger.debug() << "Restored resolv.conf symlink to stub-resolv.conf";
|
||||
}
|
||||
} else {
|
||||
QFile::link(original, kPath);
|
||||
logger.debug() << "Restored resolv.conf symlink to" << original;
|
||||
}
|
||||
}
|
||||
|
||||
bool DnsUtilsLinux::updateResolvers(const QString& ifname,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
m_ifindex = if_nametoindex(qPrintable(ifname));
|
||||
if (m_ifindex <= 0) {
|
||||
logger.error() << "Unable to resolve ifindex for" << ifname;
|
||||
return false;
|
||||
}
|
||||
m_stateFilePath = QStringLiteral("/run/amnezia-dns-%1").arg(ifname);
|
||||
|
||||
setLinkDNS(m_ifindex, resolvers);
|
||||
setLinkDefaultRoute(m_ifindex, true);
|
||||
updateLinkDomains();
|
||||
writeResolvConf(resolvers);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DnsUtilsLinux::restoreResolvers() {
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
setLinkDomains(iterator.key(), iterator.value());
|
||||
|
||||
if (m_resolvConfOriginal.isEmpty()) {
|
||||
QStringList candidates;
|
||||
if (!m_stateFilePath.isEmpty()) {
|
||||
candidates << m_stateFilePath;
|
||||
} else {
|
||||
|
||||
QDir runDir(QStringLiteral("/run"));
|
||||
for (const QString& name : runDir.entryList(
|
||||
{QStringLiteral("amnezia-dns-*")}, QDir::Files)) {
|
||||
candidates << runDir.filePath(name);
|
||||
}
|
||||
}
|
||||
for (const QString& path : candidates) {
|
||||
QFile sf(path);
|
||||
if (sf.open(QIODevice::ReadOnly)) {
|
||||
QByteArray data = sf.readAll();
|
||||
sf.close();
|
||||
m_stateFilePath = path;
|
||||
if (data.startsWith("__file__:")) {
|
||||
m_resolvConfOriginal = QStringLiteral("__file__");
|
||||
m_resolvConfSavedContent = QString::fromUtf8(data.mid(9));
|
||||
} else {
|
||||
m_resolvConfOriginal = QString::fromUtf8(data).trimmed();
|
||||
}
|
||||
logger.debug() << "Recovered DNS original from" << path << ":"
|
||||
<< m_resolvConfOriginal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_linkDomains.clear();
|
||||
|
||||
/* Revert the VPN interface's DNS configuration */
|
||||
if (m_ifindex > 0) {
|
||||
QList<QVariant> argumentList = {QVariant::fromValue(m_ifindex)};
|
||||
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
|
||||
QStringLiteral("RevertLink"), argumentList);
|
||||
const bool hadDnsState = !m_resolvConfOriginal.isEmpty();
|
||||
restoreResolvConf();
|
||||
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
|
||||
|
||||
m_ifindex = 0;
|
||||
if (hadDnsState && QFile::exists(QStringLiteral("/run/systemd/resolve"))) {
|
||||
QProcess::startDetached("systemctl", {"restart", "systemd-resolved"});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
}
|
||||
delete call;
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::setLinkDNS(int ifindex,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
QList<DnsResolver> resolverList;
|
||||
char ifnamebuf[IF_NAMESIZE];
|
||||
const char* ifname = if_indextoname(ifindex, ifnamebuf);
|
||||
for (const auto& ip : resolvers) {
|
||||
resolverList.append(ip);
|
||||
if (ifname) {
|
||||
logger.debug() << "Adding DNS resolver" << ip.toString() << "via"
|
||||
<< ifname;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(ifindex);
|
||||
argumentList << QVariant::fromValue(resolverList);
|
||||
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
|
||||
QStringLiteral("SetLinkDNS"), argumentList);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::setLinkDomains(int ifindex,
|
||||
const QList<DnsLinkDomain>& domains) {
|
||||
char ifnamebuf[IF_NAMESIZE];
|
||||
const char* ifname = if_indextoname(ifindex, ifnamebuf);
|
||||
if (ifname) {
|
||||
for (const auto& d : domains) {
|
||||
// The DNS search domains often winds up revealing user's ISP which
|
||||
// can correlate back to their location.
|
||||
logger.debug() << "Setting DNS domain:" << logger.sensitive(d.domain)
|
||||
<< "via" << ifname << (d.search ? "search" : "");
|
||||
}
|
||||
}
|
||||
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(ifindex);
|
||||
argumentList << QVariant::fromValue(domains);
|
||||
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
|
||||
QStringLiteral("SetLinkDomains"), argumentList);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) {
|
||||
QList<QVariant> argumentList;
|
||||
argumentList << QVariant::fromValue(ifindex);
|
||||
argumentList << QVariant::fromValue(enable);
|
||||
QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList(
|
||||
QStringLiteral("SetLinkDefaultRoute"), argumentList);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||
SLOT(dnsCallCompleted(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::updateLinkDomains() {
|
||||
/* Get the list of search domains, and remove any others that might conspire
|
||||
* to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't
|
||||
* seem to be able to demarshall complex property types.
|
||||
*/
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||
DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, DBUS_PROPERTY_INTERFACE, "Get");
|
||||
message << QString(DBUS_RESOLVE_MANAGER);
|
||||
message << QString("Domains");
|
||||
QDBusPendingReply<QVariant> reply =
|
||||
m_resolver->connection().asyncCall(message);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||
SLOT(dnsDomainsReceived(QDBusPendingCallWatcher*)));
|
||||
}
|
||||
|
||||
void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<QVariant> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error retrieving the DNS domains from the DBus service";
|
||||
delete call;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the state of the DNS domains */
|
||||
m_linkDomains.clear();
|
||||
QDBusArgument args = qvariant_cast<QDBusArgument>(reply.value());
|
||||
QList<DnsDomain> list = qdbus_cast<QList<DnsDomain>>(args);
|
||||
for (const auto& d : list) {
|
||||
if (d.ifindex == 0) {
|
||||
continue;
|
||||
}
|
||||
m_linkDomains[d.ifindex].append(DnsLinkDomain(d.domain, d.search));
|
||||
}
|
||||
|
||||
/* Drop any competing root search domains. */
|
||||
DnsLinkDomain root = DnsLinkDomain(".", true);
|
||||
for (auto iterator = m_linkDomains.constBegin();
|
||||
iterator != m_linkDomains.constEnd(); ++iterator) {
|
||||
if (!iterator.value().contains(root)) {
|
||||
continue;
|
||||
}
|
||||
QList<DnsLinkDomain> newlist = iterator.value();
|
||||
newlist.removeAll(root);
|
||||
setLinkDomains(iterator.key(), newlist);
|
||||
}
|
||||
|
||||
/* Add a root search domain for the new interface. */
|
||||
QList<DnsLinkDomain> newlist = {root};
|
||||
setLinkDomains(m_ifindex, newlist);
|
||||
delete call;
|
||||
}
|
||||
|
||||
static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#ifndef DNSUTILSLINUX_H
|
||||
#define DNSUTILSLINUX_H
|
||||
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "daemon/dnsutils.h"
|
||||
#include "dbustypeslinux.h"
|
||||
|
||||
class DnsUtilsLinux final : public DnsUtils {
|
||||
Q_OBJECT
|
||||
@@ -23,19 +23,14 @@ class DnsUtilsLinux final : public DnsUtils {
|
||||
bool restoreResolvers() override;
|
||||
|
||||
private:
|
||||
void setLinkDNS(int ifindex, const QList<QHostAddress>& resolvers);
|
||||
void setLinkDomains(int ifindex, const QList<DnsLinkDomain>& domains);
|
||||
void setLinkDefaultRoute(int ifindex, bool enable);
|
||||
void updateLinkDomains();
|
||||
|
||||
private slots:
|
||||
void dnsCallCompleted(QDBusPendingCallWatcher*);
|
||||
void dnsDomainsReceived(QDBusPendingCallWatcher*);
|
||||
void writeResolvConf(const QList<QHostAddress>& resolvers);
|
||||
void restoreResolvConf();
|
||||
|
||||
private:
|
||||
int m_ifindex = 0;
|
||||
QMap<int, DnsLinkDomainList> m_linkDomains;
|
||||
QDBusInterface* m_resolver = nullptr;
|
||||
|
||||
QString m_resolvConfOriginal;
|
||||
QString m_resolvConfSavedContent;
|
||||
QString m_stateFilePath;
|
||||
};
|
||||
|
||||
#endif // DNSUTILSLINUX_H
|
||||
#endif
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "linuxfirewall.h"
|
||||
#include "logger.h"
|
||||
#include <QHostAddress>
|
||||
#include <QProcess>
|
||||
|
||||
#define BRAND_CODE "amn"
|
||||
@@ -108,7 +109,7 @@ int LinuxFirewall::linkChain(LinuxFirewall::IPVersion ip, const QString& chain,
|
||||
// (we can't safely delete all rules at once since rule numbers change)
|
||||
// TODO: occasionally this script results in warnings in logs "Bad rule (does a matching rule exist in the chain?)" - this happens when
|
||||
// the e.g OUTPUT chain is empty but this script attempts to delete things from it anyway. It doesn't cause any problems, but we should still fix at some point..
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
return execute(QStringLiteral("if ! %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) == 1 && $2 == \"%3\" { found=1 } END { if(found==1) { exit 0 } else { exit 1 } }' ; then %1 -I %2 -j %3 -t %4 && %1 -L %2 -n --line-numbers -t %4 2> /dev/null | awk 'int($1) > 1 && $2 == \"%3\" { print $1; exit }' | xargs -r %1 -t %4 -D %2 ; fi").arg(cmd, parent, chain, tableName));
|
||||
}
|
||||
else
|
||||
return execute(QStringLiteral("if ! %1 -C %2 -j %3 -t %4 2> /dev/null ; then %1 -A %2 -j %3 -t %4; fi").arg(cmd, parent, chain, tableName));
|
||||
@@ -192,12 +193,8 @@ QStringList LinuxFirewall::getDNSRules(const QStringList& servers)
|
||||
QStringList result;
|
||||
for (const QString& server : servers)
|
||||
{
|
||||
result << QStringLiteral("-o amn0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o amn0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun0+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun0+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun2+ -d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-o tun2+ -d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-d %1 -p udp --dport 53 -j ACCEPT").arg(server);
|
||||
result << QStringLiteral("-d %1 -p tcp --dport 53 -j ACCEPT").arg(server);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -245,7 +242,7 @@ void LinuxFirewall::install()
|
||||
QStringLiteral("-o lo+ -j ACCEPT"),
|
||||
});
|
||||
|
||||
installAnchor(IPv4, QStringLiteral("320.allowDNS"), {});
|
||||
installAnchor(Both, QStringLiteral("320.allowDNS"), {});
|
||||
|
||||
installAnchor(Both, QStringLiteral("310.blockDNS"), {
|
||||
QStringLiteral("-p udp --dport 53 -j REJECT"),
|
||||
@@ -289,6 +286,7 @@ void LinuxFirewall::install()
|
||||
installAnchor(Both, QStringLiteral("100.blockAll"), {
|
||||
QStringLiteral("-j REJECT"),
|
||||
});
|
||||
installAnchor(Both, QStringLiteral("400.allowPIA"), {});
|
||||
// NAT rules
|
||||
installAnchor(Both, QStringLiteral("100.transIp"), {
|
||||
|
||||
@@ -351,7 +349,7 @@ void LinuxFirewall::uninstall()
|
||||
// Remove filter anchors
|
||||
uninstallAnchor(Both, QStringLiteral("000.allowLoopback"));
|
||||
uninstallAnchor(Both, QStringLiteral("400.allowPIA"));
|
||||
uninstallAnchor(IPv4, QStringLiteral("320.allowDNS"));
|
||||
uninstallAnchor(Both, QStringLiteral("320.allowDNS"));
|
||||
uninstallAnchor(Both, QStringLiteral("310.blockDNS"));
|
||||
uninstallAnchor(Both, QStringLiteral("300.allowLAN"));
|
||||
uninstallAnchor(Both, QStringLiteral("290.allowDHCP"));
|
||||
@@ -372,6 +370,8 @@ void LinuxFirewall::uninstall()
|
||||
|
||||
teardownTrafficSplitting();
|
||||
|
||||
anchorCallbacks.clear();
|
||||
|
||||
logger.debug() << "LinuxFirewall::uninstall() complete";
|
||||
}
|
||||
|
||||
@@ -445,12 +445,17 @@ void LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPVersion ip, const QString
|
||||
|
||||
void LinuxFirewall::updateDNSServers(const QStringList& servers)
|
||||
{
|
||||
static QStringList existingServers {};
|
||||
|
||||
existingServers = servers;
|
||||
execute(QStringLiteral("iptables -F %1.320.allowDNS").arg(kAnchorName));
|
||||
for (const QString& rule : getDNSRules(servers))
|
||||
execute(QStringLiteral("iptables -A %1.320.allowDNS %2").arg(kAnchorName, rule));
|
||||
execute(QStringLiteral("ip6tables -F %1.320.allowDNS").arg(kAnchorName));
|
||||
|
||||
for (const QString& server : servers) {
|
||||
if (server.isEmpty()) continue;
|
||||
const QString cmd = (QHostAddress(server).protocol() == QAbstractSocket::IPv6Protocol)
|
||||
? QStringLiteral("ip6tables")
|
||||
: QStringLiteral("iptables");
|
||||
execute(QStringLiteral("%1 -A %2.320.allowDNS -d %3 -p udp --dport 53 -j ACCEPT").arg(cmd, kAnchorName, server));
|
||||
execute(QStringLiteral("%1 -A %2.320.allowDNS -d %3 -p tcp --dport 53 -j ACCEPT").arg(cmd, kAnchorName, server));
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxFirewall::updateAllowNets(const QStringList& servers)
|
||||
@@ -495,13 +500,20 @@ int LinuxFirewall::execute(const QString &command, bool ignoreErrors)
|
||||
logger.debug() << "(" << exitCode << ") $ " << command;
|
||||
if (!out.isEmpty())
|
||||
logger.info() << out;
|
||||
if (!err.isEmpty())
|
||||
if (!err.isEmpty() && !ignoreErrors)
|
||||
logger.warning() << err;
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
void LinuxFirewall::setupTrafficSplitting()
|
||||
{
|
||||
execute(QStringLiteral("grep -q '%1' /etc/iproute2/rt_tables || printf '200\\t%1\\n' >> /etc/iproute2/rt_tables").arg(kRtableName));
|
||||
|
||||
execute(QStringLiteral(
|
||||
"if [ ! -d /sys/fs/cgroup/net_cls ] ; then "
|
||||
"mkdir -p /sys/fs/cgroup/net_cls && "
|
||||
"mount -t cgroup -o net_cls cgroup /sys/fs/cgroup/net_cls ; fi"));
|
||||
|
||||
auto cGroupDir = "/sys/fs/cgroup/net_cls/" BRAND_CODE "vpnexclusions/";
|
||||
logger.info() << "Should be setting up cgroup in" << cGroupDir << "for traffic splitting";
|
||||
execute(QStringLiteral("if [ ! -d %1 ] ; then mkdir %1 ; sleep 0.1 ; echo %2 > %1/net_cls.classid ; fi").arg(cGroupDir).arg(kCGroupId));
|
||||
@@ -513,6 +525,7 @@ void LinuxFirewall::teardownTrafficSplitting()
|
||||
{
|
||||
logger.info() << "Tearing down cgroup and routing rules";
|
||||
execute(QStringLiteral("if ip rule list | grep -q %1; then ip rule del from all fwmark %1 lookup %2 2> /dev/null ; fi").arg(kPacketTag, kRtableName));
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName));
|
||||
execute(QStringLiteral("ip route flush table %1").arg(kRtableName), true);
|
||||
execute(QStringLiteral("ip route flush cache"));
|
||||
execute(QStringLiteral("sed -i '/%1/d' /etc/iproute2/rt_tables").arg(kRtableName));
|
||||
}
|
||||
|
||||
@@ -164,8 +164,13 @@ bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type,
|
||||
}
|
||||
|
||||
if (rtm->rtm_type == RTN_THROW) {
|
||||
QString gateway = NetworkUtilities::getGatewayAndIface().first;
|
||||
if (gateway.isEmpty()) {
|
||||
logger.warning() << "No default gateway available, skipping exclusion route";
|
||||
return false;
|
||||
}
|
||||
struct in_addr ip4;
|
||||
inet_pton(AF_INET, NetworkUtilities::getGatewayAndIface().first.toUtf8(), &ip4);
|
||||
inet_pton(AF_INET, gateway.toUtf8(), &ip4);
|
||||
nlmsg_append_attr(nlmsg, sizeof(buf), RTA_GATEWAY, &ip4, sizeof(ip4));
|
||||
nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_PRIORITY, 0);
|
||||
rtm->rtm_type = RTN_UNICAST;
|
||||
|
||||
@@ -44,6 +44,9 @@ void LinuxNetworkWatcher::initialize() {
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::wakeup, this,
|
||||
&NetworkWatcherImpl::wakeup);
|
||||
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::networkChanged, this,
|
||||
[this]() { emit networkChanged(""); });
|
||||
|
||||
// Let's wait a few seconds to allow the UI to be fully loaded and shown.
|
||||
// This is not strictly needed, but it's better for user experience because
|
||||
// it makes the UI faster to appear, plus it gives a bit of delay between the
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "linuxnetworkwatcherworker.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
#include "leakdetector.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
enum NMState {
|
||||
NM_STATE_UNKNOWN = 0,
|
||||
NM_STATE_ASLEEP = 10,
|
||||
NM_STATE_DISABLED = 10,
|
||||
NM_STATE_DISCONNECTED = 20,
|
||||
NM_STATE_DISCONNECTING = 30,
|
||||
NM_STATE_CONNECTING = 40,
|
||||
@@ -122,6 +124,12 @@ void LinuxNetworkWatcherWorker::initialize() {
|
||||
SLOT(propertyChanged(QString, QVariantMap, QStringList)));
|
||||
}
|
||||
|
||||
QVariant currentState = nm.property("State");
|
||||
if (currentState.isValid()) {
|
||||
m_previousNMState = currentState.toUInt();
|
||||
logger.debug() << "Initial NM state:" << m_previousNMState;
|
||||
}
|
||||
|
||||
QDBusConnection::systemBus().connect(DBUS_NETWORKMANAGER,
|
||||
DBUS_NETWORKMANAGER_PATH,
|
||||
DBUS_NETWORKMANAGER,
|
||||
@@ -199,10 +207,13 @@ void LinuxNetworkWatcherWorker::checkDevices() {
|
||||
|
||||
void LinuxNetworkWatcherWorker::NMStateChanged(quint32 state)
|
||||
{
|
||||
if (state == NM_STATE_ASLEEP) {
|
||||
emit wakeup();
|
||||
}
|
||||
logger.debug() << "NMStateChanged " << state;
|
||||
|
||||
logger.debug() << "NMStateChanged " << state;
|
||||
}
|
||||
if (state == NM_STATE_ASLEEP || state == NM_STATE_DISABLED) {
|
||||
emit wakeup();
|
||||
} else if (state >= NM_STATE_CONNECTED_SITE && m_previousNMState < NM_STATE_CONNECTED_SITE) {
|
||||
emit networkChanged();
|
||||
}
|
||||
|
||||
m_previousNMState = state;
|
||||
}
|
||||
@@ -24,6 +24,7 @@ class LinuxNetworkWatcherWorker final : public QObject {
|
||||
signals:
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
void wakeup();
|
||||
void networkChanged();
|
||||
|
||||
public slots:
|
||||
void initialize();
|
||||
@@ -34,10 +35,12 @@ class LinuxNetworkWatcherWorker final : public QObject {
|
||||
void NMStateChanged(quint32 state);
|
||||
|
||||
private:
|
||||
|
||||
// We collect the list of DBus wifi network device paths during the
|
||||
// initialization. When a property of them changes, we check if the access
|
||||
// point is active and unsecure.
|
||||
QStringList m_devicePaths;
|
||||
quint32 m_previousNMState = 0;
|
||||
};
|
||||
|
||||
#endif // LINUXNETWORKWATCHERWORKER_H
|
||||
|
||||
+46
-26
@@ -57,11 +57,15 @@ void VpnConnection::onKillSwitchModeChanged(bool enabled)
|
||||
{
|
||||
#ifdef AMNEZIA_DESKTOP
|
||||
IpcClient::withInterface([enabled](QSharedPointer<IpcInterfaceReplica> iface){
|
||||
QRemoteObjectPendingReply<bool> reply = iface->refreshKillSwitch(enabled);
|
||||
if (reply.waitForFinished() && reply.returnValue())
|
||||
qDebug() << "VpnConnection::onKillSwitchModeChanged: Killswitch refreshed";
|
||||
else
|
||||
qWarning() << "VpnConnection::onKillSwitchModeChanged: Failed to execute remote refreshKillSwitch call";
|
||||
auto reply = iface->refreshKillSwitch(enabled);
|
||||
auto *watcher = new QRemoteObjectPendingCallWatcher(reply);
|
||||
QObject::connect(watcher, &QRemoteObjectPendingCallWatcher::finished, [watcher]() {
|
||||
if (watcher->returnValue().toBool())
|
||||
qDebug() << "VpnConnection::onKillSwitchModeChanged: Killswitch refreshed";
|
||||
else
|
||||
qWarning() << "VpnConnection::onKillSwitchModeChanged: Failed to refresh killswitch";
|
||||
watcher->deleteLater();
|
||||
});
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -76,15 +80,19 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
case Vpn::ConnectionState::Connected: {
|
||||
iface->resetIpStack();
|
||||
|
||||
auto flushDns = iface->flushDns();
|
||||
if (flushDns.waitForFinished() && flushDns.returnValue())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
|
||||
|
||||
|
||||
if (!ContainerProps::isAwgContainer(container) &&
|
||||
container != DockerContainer::WireGuard) {
|
||||
auto flushDnsReply = iface->flushDns();
|
||||
auto *flushDnsWatcher = new QRemoteObjectPendingCallWatcher(flushDnsReply, this);
|
||||
QObject::connect(flushDnsWatcher, &QRemoteObjectPendingCallWatcher::finished, this,
|
||||
[flushDnsWatcher]() {
|
||||
if (flushDnsWatcher->returnValue().toBool())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
|
||||
flushDnsWatcher->deleteLater();
|
||||
});
|
||||
|
||||
QString dns1 = m_vpnConfiguration.value(config_key::dns1).toString();
|
||||
QString dns2 = m_vpnConfiguration.value(config_key::dns2).toString();
|
||||
|
||||
@@ -109,17 +117,25 @@ void VpnConnection::onConnectionStateChanged(Vpn::ConnectionState state)
|
||||
} break;
|
||||
case Vpn::ConnectionState::Disconnected:
|
||||
case Vpn::ConnectionState::Error: {
|
||||
auto flushDns = iface->flushDns();
|
||||
if (flushDns.waitForFinished() && flushDns.returnValue())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
|
||||
auto flushDnsReply = iface->flushDns();
|
||||
auto *flushDnsWatcher = new QRemoteObjectPendingCallWatcher(flushDnsReply);
|
||||
QObject::connect(flushDnsWatcher, &QRemoteObjectPendingCallWatcher::finished, [flushDnsWatcher]() {
|
||||
if (flushDnsWatcher->returnValue().toBool())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully flushed DNS";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to flush DNS";
|
||||
flushDnsWatcher->deleteLater();
|
||||
});
|
||||
|
||||
auto clearSavedRoutes = iface->clearSavedRoutes();
|
||||
if (clearSavedRoutes.waitForFinished() && clearSavedRoutes.returnValue())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully cleared saved routes";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
|
||||
auto clearSavedRoutesReply = iface->clearSavedRoutes();
|
||||
auto *clearRoutesWatcher = new QRemoteObjectPendingCallWatcher(clearSavedRoutesReply);
|
||||
QObject::connect(clearRoutesWatcher, &QRemoteObjectPendingCallWatcher::finished, [clearRoutesWatcher]() {
|
||||
if (clearRoutesWatcher->returnValue().toBool())
|
||||
qDebug() << "VpnConnection::onConnectionStateChanged: Successfully cleared saved routes";
|
||||
else
|
||||
qWarning() << "VpnConnection::onConnectionStateChanged: Failed to clear saved routes";
|
||||
clearRoutesWatcher->deleteLater();
|
||||
});
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
@@ -182,8 +198,12 @@ void VpnConnection::addSitesRoutes(const QString &gw, Settings::RouteMode mode)
|
||||
}
|
||||
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
|
||||
auto reply = iface->flushDns();
|
||||
if (reply.waitForFinished() || !reply.returnValue())
|
||||
qWarning() << "VpnConnection::addSitesRoutes: Failed to flush DNS";
|
||||
auto *w = new QRemoteObjectPendingCallWatcher(reply);
|
||||
QObject::connect(w, &QRemoteObjectPendingCallWatcher::finished, [w]() {
|
||||
if (!w->returnValue().toBool())
|
||||
qWarning() << "VpnConnection::addSitesRoutes: Failed to flush DNS";
|
||||
w->deleteLater();
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -219,8 +239,8 @@ ErrorCode VpnConnection::lastError() const
|
||||
return m_vpnProtocol.data()->lastError();
|
||||
}
|
||||
|
||||
void VpnConnection::connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container,
|
||||
const QJsonObject &vpnConfiguration)
|
||||
void VpnConnection::connectToVpn(int serverIndex, const amnezia::ServerCredentials &credentials,
|
||||
amnezia::DockerContainer container, const QJsonObject &vpnConfiguration)
|
||||
{
|
||||
qDebug() << QString("Trying to connect to VPN, server index is %1, container is %2, route mode is")
|
||||
.arg(serverIndex)
|
||||
|
||||
@@ -44,7 +44,8 @@ public:
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void connectToVpn(int serverIndex, const amnezia::ServerCredentials &credentials,
|
||||
amnezia::DockerContainer container, const QJsonObject &vpnConfiguration);
|
||||
void reconnectToVpn();
|
||||
void disconnectFromVpn();
|
||||
|
||||
|
||||
@@ -53,7 +53,9 @@ bool KillSwitch::refresh(bool enabled)
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
if (!m_appSettigns.isNull()) {
|
||||
m_appSettigns->setValue("Conf/strictKillSwitchEnabled", enabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isStrictKillSwitchEnabled()) {
|
||||
@@ -70,6 +72,11 @@ bool KillSwitch::isStrictKillSwitchEnabled()
|
||||
QSettings RegHLM("HKEY_LOCAL_MACHINE\\Software\\" + QString(ORGANIZATION_NAME)
|
||||
+ "\\" + QString(APPLICATION_NAME), QSettings::NativeFormat);
|
||||
return RegHLM.value("strictKillSwitchEnabled", false).toBool();
|
||||
#endif
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
|
||||
if (m_appSettigns.isNull()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
|
||||
}
|
||||
@@ -86,7 +93,7 @@ bool KillSwitch::disableKillSwitch() {
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("320.allowDNS"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
|
||||
} else {
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
|
||||
@@ -98,7 +105,7 @@ bool KillSwitch::disableKillSwitch() {
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), false);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), false);
|
||||
LinuxFirewall::uninstall();
|
||||
}
|
||||
@@ -335,7 +342,7 @@ bool KillSwitch::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIn
|
||||
}
|
||||
|
||||
LinuxFirewall::updateDNSServers(dnsServers);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("320.allowDNS"), true);
|
||||
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
bool isStrictKillSwitchEnabled();
|
||||
|
||||
private:
|
||||
KillSwitch(QObject* parent) {};
|
||||
explicit KillSwitch(QObject* parent) : QObject(parent) {}
|
||||
QStringList m_allowedRanges;
|
||||
QSharedPointer<SecureQSettings> m_appSettigns;
|
||||
|
||||
|
||||
@@ -120,18 +120,20 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co
|
||||
((struct sockaddr_in *)&route.rt_gateway)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr = inet_addr(gw.toStdString().c_str());
|
||||
((struct sockaddr_in *)&route.rt_gateway)->sin_port = 0;
|
||||
|
||||
// set host rejecting
|
||||
((struct sockaddr_in *)&route.rt_dst)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr = inet_addr(ip.toStdString().c_str());
|
||||
((struct sockaddr_in *)&route.rt_dst)->sin_port = 0;
|
||||
|
||||
// set mask
|
||||
((struct sockaddr_in *)&route.rt_genmask)->sin_family = AF_INET;
|
||||
((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr = inet_addr(mask.toStdString().c_str());
|
||||
((struct sockaddr_in *)&route.rt_genmask)->sin_port = 0;
|
||||
|
||||
route.rt_flags = RTF_UP | RTF_GATEWAY;
|
||||
route.rt_metric = 0;
|
||||
//route.rt_dev = "ens33";
|
||||
route.rt_metric = 0;
|
||||
|
||||
if (ioctl(sock, SIOCDELRT, &route) < 0)
|
||||
{
|
||||
@@ -155,36 +157,29 @@ bool RouterLinux::routeDeleteList(const QString &gw, const QStringList &ips)
|
||||
bool RouterLinux::isServiceActive(const QString &serviceName) {
|
||||
QProcess process;
|
||||
process.start("systemctl", { "is-active", "--quiet", serviceName });
|
||||
process.waitForFinished();
|
||||
|
||||
if (!process.waitForFinished(2000)) {
|
||||
process.kill();
|
||||
process.waitForFinished(500);
|
||||
}
|
||||
return process.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool RouterLinux::flushDns()
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
//check what the dns manager use
|
||||
if (isServiceActive("nscd.service")) {
|
||||
qDebug() << "Restarting nscd.service";
|
||||
p.start("systemctl", { "restart", "nscd" });
|
||||
qDebug() << "Flushing nscd cache";
|
||||
QProcess::startDetached("nscd", { "--invalidate=hosts" });
|
||||
return true;
|
||||
} else if (isServiceActive("systemd-resolved.service")) {
|
||||
qDebug() << "Restarting systemd-resolved.service";
|
||||
p.start("systemctl", { "restart", "systemd-resolved" });
|
||||
qDebug() << "Flushing systemd-resolved cache";
|
||||
QProcess::startDetached("resolvectl", { "flush-caches" });
|
||||
qDebug().noquote() << "Flush dns requested (non-blocking)";
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "No suitable DNS manager found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
p.waitForFinished();
|
||||
QByteArray output(p.readAll());
|
||||
if (output.isEmpty())
|
||||
qDebug().noquote() << "Flush dns completed";
|
||||
else
|
||||
qDebug().noquote() << "OUTPUT systemctl restart nscd/systemd-resolved: " + output;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RouterLinux::createTun(const QString &dev, const QString &subnet) {
|
||||
|
||||
Reference in New Issue
Block a user