Files
amnezia-client/client/platforms/linux/daemon/dnsutilslinux.cpp
T

169 lines
5.1 KiB
C++
Raw Normal View History

2023-09-17 17:06:24 -04:00
#include "dnsutilslinux.h"
2026-04-10 23:06:16 +03:00
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
2023-09-17 17:06:24 -04:00
#include "leakdetector.h"
#include "logger.h"
namespace {
Logger logger("DnsUtilsLinux");
}
DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) {
MZ_COUNT_CTOR(DnsUtilsLinux);
logger.debug() << "DnsUtilsLinux created.";
}
DnsUtilsLinux::~DnsUtilsLinux() {
MZ_COUNT_DTOR(DnsUtilsLinux);
logger.debug() << "DnsUtilsLinux destroyed.";
}
2026-04-10 23:06:16 +03:00
void DnsUtilsLinux::writeResolvConf(const QList<QHostAddress>& resolvers) {
if (resolvers.isEmpty()) return;
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
static const QString kPath = QStringLiteral("/etc/resolv.conf");
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
if (m_resolvConfOriginal.isEmpty()) {
QFileInfo fi(kPath);
if (fi.isSymLink()) {
m_resolvConfOriginal = fi.symLinkTarget();
logger.debug() << "Saved resolv.conf symlink target:"
<< m_resolvConfOriginal;
2026-04-10 23:08:32 +03:00
if (!m_stateFilePath.isEmpty()) {
QFile sf(m_stateFilePath);
if (sf.open(QIODevice::WriteOnly | QIODevice::Text))
sf.write(m_resolvConfOriginal.toUtf8());
}
2026-04-10 23:06:16 +03:00
} else {
2026-04-10 23:08:32 +03:00
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__");
}
2026-04-10 23:06:16 +03:00
}
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
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());
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
f.close();
logger.debug() << "Wrote resolv.conf with" << resolvers.size()
<< "DNS servers";
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
void DnsUtilsLinux::restoreResolvConf() {
if (m_resolvConfOriginal.isEmpty()) return;
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
const QString original = m_resolvConfOriginal;
m_resolvConfOriginal.clear();
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
if (!m_stateFilePath.isEmpty())
QFile::remove(m_stateFilePath);
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
static const char* kPath = "/etc/resolv.conf";
QFile::remove(kPath);
2023-09-17 17:06:24 -04:00
2026-04-10 23:08:32 +03:00
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;
}
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
bool DnsUtilsLinux::updateResolvers(const QString& ifname,
const QList<QHostAddress>& resolvers) {
m_stateFilePath = QStringLiteral("/run/amnezia-dns-%1").arg(ifname);
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
writeResolvConf(resolvers);
QProcess::startDetached("resolvectl", {"flush-caches"});
2023-09-17 17:06:24 -04:00
2026-04-10 23:06:16 +03:00
return true;
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
bool DnsUtilsLinux::restoreResolvers() {
logger.debug() << "restoreResolvers: original="
<< (m_resolvConfOriginal.isEmpty() ? "(empty)"
: m_resolvConfOriginal);
if (m_resolvConfOriginal.isEmpty()) {
QStringList candidates;
if (!m_stateFilePath.isEmpty()) {
candidates << m_stateFilePath;
2026-03-31 10:35:04 +03:00
} else {
2026-04-10 23:08:32 +03:00
2026-04-10 23:06:16 +03:00
QDir runDir(QStringLiteral("/run"));
for (const QString& name : runDir.entryList(
{QStringLiteral("amnezia-dns-*")}, QDir::Files)) {
candidates << runDir.filePath(name);
}
2026-03-31 10:35:04 +03:00
}
2026-04-10 23:06:16 +03:00
for (const QString& path : candidates) {
QFile sf(path);
2026-04-10 23:08:32 +03:00
if (sf.open(QIODevice::ReadOnly)) {
QByteArray data = sf.readAll();
2026-04-10 23:06:16 +03:00
sf.close();
2026-04-10 23:08:32 +03:00
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();
}
2026-04-10 23:06:16 +03:00
logger.debug() << "Recovered DNS original from" << path << ":"
<< m_resolvConfOriginal;
break;
}
2023-09-17 17:06:24 -04:00
}
}
2026-04-10 23:06:16 +03:00
const bool hadDnsState = !m_resolvConfOriginal.isEmpty();
restoreResolvConf();
2026-04-10 23:08:32 +03:00
if (hadDnsState && QFile::exists(QStringLiteral("/run/systemd/resolve"))) {
2026-04-10 23:06:16 +03:00
QProcess::startDetached("systemctl", {"restart", "systemd-resolved"});
2023-09-17 17:06:24 -04:00
}
2026-04-10 23:06:16 +03:00
return true;
2023-09-17 17:06:24 -04:00
}