Add system dns toggle

This commit is contained in:
aiamnezia
2025-11-02 18:38:49 +04:00
parent f1481b1b1f
commit d230043ead
8 changed files with 281 additions and 0 deletions
+202
View File
@@ -13,6 +13,7 @@
#include <QNetworkInterface>
#include "qendian.h"
#include <QSettings>
#pragma comment(lib, "iphlpapi.lib")
#endif
#ifdef Q_OS_LINUX
#include <arpa/inet.h>
@@ -22,6 +23,21 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
#include <QDBusArgument>
#include <QVariant>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include "platforms/linux/daemon/dbustypeslinux.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";
#endif
#if defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
#include <sys/param.h>
@@ -30,10 +46,17 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/route.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#endif
#include <QHostAddress>
#include <QHostInfo>
#include <QPair>
#include "logger.h"
QRegularExpression NetworkUtilities::ipAddressRegExp()
{
@@ -475,3 +498,182 @@ QString NetworkUtilities::getGatewayAndIface()
return gateway;
#endif
}
QPair<QString, QString> NetworkUtilities::getSystemDnsAddress()
{
QPair<QString, QString> result;
#ifdef Q_OS_LINUX
// Try systemd-resolved via D-Bus first
QDBusConnection bus = QDBusConnection::systemBus();
if (bus.isConnected()) {
// Try to get DNS from Resolve DNS property using org.freedesktop.DBus.Properties
// Use the same approach as in dnsutilslinux.cpp
QDBusMessage message = QDBusMessage::createMethodCall(
DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, DBUS_PROPERTY_INTERFACE, "Get");
message << QString(DBUS_RESOLVE_MANAGER);
message << QString("DNS");
QDBusReply<QVariant> dnsReply = bus.call(message);
if (dnsReply.isValid()) {
QDBusArgument dnsArg = qvariant_cast<QDBusArgument>(dnsReply.value());
QList<DnsResolver> resolverList = qdbus_cast<QList<DnsResolver>>(dnsArg);
QStringList dnsServers;
for (const auto& resolver : resolverList) {
if (resolver.protocol() == QAbstractSocket::IPv4Protocol) {
QString dnsStr = resolver.toString();
if (checkIPv4Format(dnsStr) && !dnsServers.contains(dnsStr)) {
dnsServers.append(dnsStr);
}
}
}
if (!dnsServers.isEmpty()) {
result.first = dnsServers.first();
if (dnsServers.size() > 1) {
result.second = dnsServers.at(1);
}
return result;
}
}
}
// Fallback to /etc/resolv.conf
QFile resolvConf("/etc/resolv.conf");
if (resolvConf.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&resolvConf);
QStringList dnsServers;
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
if (line.startsWith("nameserver")) {
QStringList parts = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
if (parts.size() >= 2) {
QString dns = parts.at(1);
if (checkIPv4Format(dns)) {
dnsServers.append(dns);
}
}
}
}
if (!dnsServers.isEmpty()) {
result.first = dnsServers.first();
if (dnsServers.size() > 1) {
result.second = dnsServers.at(1);
}
return result;
}
}
qWarning() << "Failed to get system DNS on Linux";
return result; // Return empty pair
#elif defined(Q_OS_WIN)
// Use GetAdaptersAddresses to get DNS servers
ULONG bufferSize = 0;
DWORD ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufferSize);
if (ret == ERROR_BUFFER_OVERFLOW) {
PIP_ADAPTER_ADDRESSES adapterAddresses = (PIP_ADAPTER_ADDRESSES)malloc(bufferSize);
if (adapterAddresses) {
ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, adapterAddresses, &bufferSize);
if (ret == NO_ERROR) {
PIP_ADAPTER_ADDRESSES currentAdapter = adapterAddresses;
QStringList dnsServers;
while (currentAdapter) {
// Check if adapter is active and has IP addresses
if (currentAdapter->OperStatus == IfOperStatusUp &&
currentAdapter->FirstUnicastAddress != nullptr) {
PIP_ADAPTER_DNS_SERVER_ADDRESS dnsServer = currentAdapter->FirstDnsServerAddress;
while (dnsServer) {
if (dnsServer->Address.lpSockaddr->sa_family == AF_INET) {
struct sockaddr_in* sa_in = (struct sockaddr_in*)dnsServer->Address.lpSockaddr;
char ipstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sa_in->sin_addr, ipstr, INET_ADDRSTRLEN);
QString dns = QString::fromLatin1(ipstr);
if (checkIPv4Format(dns) && !dnsServers.contains(dns)) {
dnsServers.append(dns);
}
}
dnsServer = dnsServer->Next;
}
}
currentAdapter = currentAdapter->Next;
}
if (!dnsServers.isEmpty()) {
result.first = dnsServers.first();
if (dnsServers.size() > 1) {
result.second = dnsServers.at(1);
}
qDebug() << "Got system DNS from Windows:" << result.first << result.second;
free(adapterAddresses);
return result;
}
}
free(adapterAddresses);
}
}
qWarning() << "Failed to get system DNS on Windows";
return result; // Return empty pair
#elif defined(Q_OS_MAC) && !defined(Q_OS_IOS) && !defined(MACOS_NE)
// Use SCDynamicStore to get DNS from system configuration
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("amneziavpn"), nullptr, nullptr);
if (store) {
CFDictionaryRef dnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/DNS"));
if (dnsDict) {
CFArrayRef dnsServersArray = (CFArrayRef)CFDictionaryGetValue(dnsDict, CFSTR("ServerAddresses"));
if (dnsServersArray && CFArrayGetCount(dnsServersArray) > 0) {
QStringList dnsServers;
for (CFIndex i = 0; i < CFArrayGetCount(dnsServersArray); i++) {
CFStringRef dnsString = (CFStringRef)CFArrayGetValueAtIndex(dnsServersArray, i);
if (dnsString) {
char buffer[256];
if (CFStringGetCString(dnsString, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
QString dns = QString::fromLatin1(buffer);
if (checkIPv4Format(dns)) {
dnsServers.append(dns);
}
}
}
}
if (!dnsServers.isEmpty()) {
result.first = dnsServers.first();
if (dnsServers.size() > 1) {
result.second = dnsServers.at(1);
}
qDebug() << "Got system DNS from macOS:" << result.first << result.second;
CFRelease(dnsDict);
CFRelease(store);
return result;
}
}
CFRelease(dnsDict);
}
CFRelease(store);
}
qWarning() << "Failed to get system DNS on macOS";
return result; // Return empty pair
#else
qWarning() << "System DNS reading not implemented for this platform";
return result; // Return empty pair
#endif
}
+4
View File
@@ -6,6 +6,7 @@
#include <QString>
#include <QHostAddress>
#include <QNetworkReply>
#include <QPair>
class NetworkUtilities : public QObject
@@ -31,6 +32,9 @@ public:
static QString netMaskFromIpWithSubnet(const QString ip);
static QString ipAddressFromIpWithSubnet(const QString ip);
static QStringList summarizeRoutes(const QStringList &ips, const QString cidr);
// Returns pair of (primary DNS, secondary DNS) or empty strings on error
static QPair<QString, QString> getSystemDnsAddress();
};
#endif // NETWORKUTILITIES_H
+9
View File
@@ -145,6 +145,15 @@ public:
setValue("Conf/useAmneziaDns", enabled);
}
bool useSystemDnsAddress() const
{
return value("Conf/useSystemDnsAddress", false).toBool();
}
void setUseSystemDnsAddress(bool enabled)
{
setValue("Conf/useSystemDnsAddress", enabled);
}
QString primaryDns() const;
QString secondaryDns() const;
@@ -57,6 +57,12 @@ void ConnectionController::openConnection()
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto dns = m_serversModel->getDnsPair(serverIndex);
// Check if DNS retrieval failed (empty pair means system DNS retrieval failed)
if (dns.first.isEmpty() && dns.second.isEmpty()) {
emit connectionErrorOccurred(ErrorCode::InternalError);
return;
}
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
@@ -67,6 +67,17 @@ bool SettingsController::isAmneziaDnsEnabled()
return m_settings->useAmneziaDns();
}
bool SettingsController::isUseSystemDnsAddressEnabled()
{
return m_settings->useSystemDnsAddress();
}
void SettingsController::setUseSystemDnsAddress(bool enable)
{
m_settings->setUseSystemDnsAddress(enable);
emit useSystemDnsAddressChanged(enable);
}
QString SettingsController::getPrimaryDns()
{
return m_settings->primaryDns();
@@ -26,6 +26,7 @@ public:
Q_PROPERTY(bool isNotificationPermissionGranted READ isNotificationPermissionGranted NOTIFY onNotificationStateChanged)
Q_PROPERTY(bool isKillSwitchEnabled READ isKillSwitchEnabled WRITE toggleKillSwitch NOTIFY killSwitchEnabledChanged)
Q_PROPERTY(bool strictKillSwitchEnabled READ isStrictKillSwitchEnabled WRITE toggleStrictKillSwitch NOTIFY strictKillSwitchEnabledChanged)
Q_PROPERTY(bool useSystemDnsAddressEnabled READ isUseSystemDnsAddressEnabled WRITE setUseSystemDnsAddress NOTIFY useSystemDnsAddressChanged)
Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled)
Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged)
@@ -38,6 +39,9 @@ public slots:
void toggleAmneziaDns(bool enable);
bool isAmneziaDnsEnabled();
bool isUseSystemDnsAddressEnabled();
void setUseSystemDnsAddress(bool enable);
QString getPrimaryDns();
void setPrimaryDns(const QString &dns);
@@ -117,6 +121,8 @@ signals:
void amneziaDnsToggled(bool enable);
void useSystemDnsAddressChanged(bool enabled);
void loggingDisableByWatcher();
void onNotificationStateChanged();
+20
View File
@@ -603,6 +603,26 @@ QPair<QString, QString> ServersModel::getDnsPair(int serverIndex)
}
}
// Check if system DNS should be used
if (m_settings->useSystemDnsAddress() && apiUtils::isPremiumServer(server)) {
auto systemDns = NetworkUtilities::getSystemDnsAddress();
bool hasPrimary = !systemDns.first.isEmpty() && NetworkUtilities::checkIPv4Format(systemDns.first);
bool hasSecondary = !systemDns.second.isEmpty() && NetworkUtilities::checkIPv4Format(systemDns.second);
if (hasPrimary || hasSecondary) {
if (hasPrimary) {
dns.first = systemDns.first;
}
if (hasSecondary) {
dns.second = systemDns.second;
}
qDebug() << "VpnConfigurator::getDnsForConfig using system DNS:" << dns.first << dns.second;
} else {
qWarning() << "Failed to get system DNS for premium config, connection will fail";
}
return dns;
}
dns.first = server.value(config_key::dns1).toString();
dns.second = server.value(config_key::dns2).toString();
@@ -75,6 +75,29 @@ PageType {
DividerType {}
SwitcherType {
id: useSystemDnsSwitch
visible: ServersModel.processedServerIsPremium
Layout.fillWidth: true
Layout.margins: 16
text: qsTr("Use system DNS")
descriptionText: qsTr("Use system DNS servers")
checked: SettingsController.useSystemDnsAddressEnabled
onToggled: function() {
if (checked !== SettingsController.useSystemDnsAddressEnabled) {
SettingsController.setUseSystemDnsAddress(checked)
}
}
}
DividerType {
visible: ServersModel.processedServerIsPremium
}
LabelWithButtonType {
id: dnsServersButton