[GH-ISSUE #3190] Cannot save empty Primary DNS in Settings — blocks split-tunnel-only setups that need no DNS routed through the VPN #1476

Open
opened 2026-10-13 20:43:08 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @immaro on GitHub (Sep 19, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3190

Describe the bug

The DNS settings page (Settings → DNS) refuses to save if the "Primary DNS" field is empty, showing the error "Primary DNS cannot be empty". This blocks a legitimate use case: a split-tunnel-only WireGuard/AmneziaWG connection where the VPN is used purely to reach specific subnets, and no DNS traffic should be routed through the tunnel at all (system/default resolver should be used unchanged).

The validation is purely a UI-level restriction. The underlying daemon and config generation code already fully support an empty DNS value without any special-casing needed:

  • client/daemon/interfaceconfig.cpp (toString()): the DNS = ... line is only written into the config handed to the tunnel backend when !m_primaryDnsServer.isEmpty(). An empty value simply omits the line.
  • client/daemon/daemon.cpp (Daemon::maybeUpdateResolvers): with an empty m_primaryDnsServer, the resulting QHostAddress is invalid and is filtered out by the platform DNS backends.
  • client/platforms/windows/daemon/dnsutilswindows.cpp (updateResolversWin32): an empty resolver list results in calling SetInterfaceDnsSettings with an empty nameserver string for both IPv4/IPv6 — the same call restoreResolvers() uses to clear DNS on disconnect. So an empty primary DNS cleanly results in "no DNS configured on the adapter," not a crash or partial state.

So the daemon already does the right thing for an empty DNS — the only place actively preventing this is the QML form validation:

client/ui/qml/Pages2/PageSettingsDns.qml, in the Save button handler:

clickedFunc: function() {
    if (primaryDns.textField.text === "") {
        primaryDns.errorText = qsTr("Primary DNS cannot be empty")
        return
    }
    primaryDns.errorText = ""
    secondaryDns.errorText = ""
    ...

To Reproduce

  1. Import or configure an AmneziaWG/WireGuard server intended for split-tunnel use only (AllowedIPs limited to specific subnets, not 0.0.0.0/0).
  2. Go to Settings → DNS.
  3. Clear the "Primary DNS" field entirely and press Save.
  4. Observe: "Primary DNS cannot be empty" — the field cannot be saved empty, forcing a DNS server to be configured on the VPN adapter even though it is never actually used (traffic to it isn't covered by AllowedIPs, so it doesn't even go through the tunnel — it's a no-op setting that the UI nonetheless mandates).

Expected behavior
Saving an empty Primary DNS should be allowed. The resulting behavior (no DNS = line written, no DNS configured on the tunnel adapter) is already correctly implemented in the daemon — the UI should not add an artificial requirement beyond what the backend needs.

Suggested fix

Remove the empty-check early return in the Save handler (keep the IP-format RegularExpressionValidator on the field for non-empty input, just stop rejecting an empty string):

clickedFunc: function() {
    primaryDns.errorText = ""
    secondaryDns.errorText = ""

    if (primaryDns.textField.text !== SettingsController.primaryDns) {
        SettingsController.primaryDns = primaryDns.textField.text
    }
    if (secondaryDns.textField.text !== SettingsController.secondaryDns) {
        SettingsController.secondaryDns = secondaryDns.textField.text
    }
    PageController.showNotificationMessage(qsTr("Settings saved"))
}

Desktop (please complete the following information):

  • OS: Windows 11 10.0.22631.3296, also affects any platform using this same Settings page
  • Version: 5.0.3.0 (commit 94b51df247)
Originally created by @immaro on GitHub (Sep 19, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/3190 **Describe the bug** The DNS settings page (Settings → DNS) refuses to save if the "Primary DNS" field is empty, showing the error "Primary DNS cannot be empty". This blocks a legitimate use case: a split-tunnel-only WireGuard/AmneziaWG connection where the VPN is used purely to reach specific subnets, and no DNS traffic should be routed through the tunnel at all (system/default resolver should be used unchanged). The validation is purely a UI-level restriction. The underlying daemon and config generation code already fully support an empty DNS value without any special-casing needed: - `client/daemon/interfaceconfig.cpp` (`toString()`): the `DNS = ...` line is only written into the config handed to the tunnel backend when `!m_primaryDnsServer.isEmpty()`. An empty value simply omits the line. - `client/daemon/daemon.cpp` (`Daemon::maybeUpdateResolvers`): with an empty `m_primaryDnsServer`, the resulting `QHostAddress` is invalid and is filtered out by the platform DNS backends. - `client/platforms/windows/daemon/dnsutilswindows.cpp` (`updateResolversWin32`): an empty resolver list results in calling `SetInterfaceDnsSettings` with an empty nameserver string for both IPv4/IPv6 — the same call `restoreResolvers()` uses to *clear* DNS on disconnect. So an empty primary DNS cleanly results in "no DNS configured on the adapter," not a crash or partial state. So the daemon already does the right thing for an empty DNS — the only place actively preventing this is the QML form validation: `client/ui/qml/Pages2/PageSettingsDns.qml`, in the Save button handler: ```qml clickedFunc: function() { if (primaryDns.textField.text === "") { primaryDns.errorText = qsTr("Primary DNS cannot be empty") return } primaryDns.errorText = "" secondaryDns.errorText = "" ... ``` **To Reproduce** 1. Import or configure an AmneziaWG/WireGuard server intended for split-tunnel use only (`AllowedIPs` limited to specific subnets, not `0.0.0.0/0`). 2. Go to Settings → DNS. 3. Clear the "Primary DNS" field entirely and press Save. 4. Observe: "Primary DNS cannot be empty" — the field cannot be saved empty, forcing a DNS server to be configured on the VPN adapter even though it is never actually used (traffic to it isn't covered by `AllowedIPs`, so it doesn't even go through the tunnel — it's a no-op setting that the UI nonetheless mandates). **Expected behavior** Saving an empty Primary DNS should be allowed. The resulting behavior (no `DNS = ` line written, no DNS configured on the tunnel adapter) is already correctly implemented in the daemon — the UI should not add an artificial requirement beyond what the backend needs. **Suggested fix** Remove the empty-check early return in the Save handler (keep the IP-format `RegularExpressionValidator` on the field for non-empty input, just stop rejecting an empty string): ```qml clickedFunc: function() { primaryDns.errorText = "" secondaryDns.errorText = "" if (primaryDns.textField.text !== SettingsController.primaryDns) { SettingsController.primaryDns = primaryDns.textField.text } if (secondaryDns.textField.text !== SettingsController.secondaryDns) { SettingsController.secondaryDns = secondaryDns.textField.text } PageController.showNotificationMessage(qsTr("Settings saved")) } ``` **Desktop (please complete the following information):** - OS: Windows 11 10.0.22631.3296, also affects any platform using this same Settings page - Version: 5.0.3.0 (commit 94b51df24790bf52427afe82d81c87a95460bdfd)
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#1476
No description provided.