Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ A Windows desktop application for managing DNS servers across your network adapt
## Features

- **Network discovery** — Detects adapters (Wi‑Fi, Ethernet, VPN, virtual)
- **Adapter health badges** — Shows connected/disconnected and enabled/disabled state for each adapter
- **DNS catalog** — 30+ providers (Google, Cloudflare, Quad9, OpenDNS, AdGuard, etc.)
- **Custom DNS** — Add and manage your own servers
- **Speed test** — Real DNS queries with latency, packet loss, and stability
Expand Down Expand Up @@ -101,3 +102,10 @@ This builds the C++ DLL, copies it to `windows_gui`, and builds the WPF applicat
2. Download the latest **DNSChanger-Setup-x.x.x.exe**.
3. Run the installer (UAC may prompt).
4. Launch **DNS Changer** from the Start menu or desktop. Use **Run as administrator** when changing DNS.


## Latest changes (v2.1.0)

- Improved network adapter handling so disconnected adapters stay visible.
- DNS apply/restore now protects users from applying changes to disabled adapters.
- Updated UI with richer adapter status details and a version badge in the header.
63 changes: 57 additions & 6 deletions cpp_core/src/dns_apply_windows.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,54 @@
#define WIN32_LEAN_AND_MEAN
#include "../include/dns_apply_windows.h"
#include "../include/logger.h"
#include "../include/win_utils.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <netioapi.h>
#include <iphlpapi.h>
#include <netioapi.h>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cctype>

namespace {
std::string NormalizeGuid(std::string guid) {
guid.erase(std::remove_if(guid.begin(), guid.end(), [](unsigned char ch) {
return std::isspace(ch);
}), guid.end());

if (!guid.empty() && guid.front() == '{' && guid.back() == '}') {
return guid;
}

return "{" + guid + "}";
}

std::wstring FindAdapterFriendlyName(const std::string& interfaceGuid) {
ULONG bufferSize = 0;
GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &bufferSize);
if (bufferSize == 0) {
return L"";
}

std::vector<unsigned char> buffer(bufferSize);
PIP_ADAPTER_ADDRESSES adapters = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());

if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &bufferSize) != NO_ERROR) {
return L"";
}

const std::string normalizedGuid = NormalizeGuid(interfaceGuid);
for (auto* adapter = adapters; adapter != nullptr; adapter = adapter->Next) {
if (adapter->AdapterName && normalizedGuid == adapter->AdapterName && adapter->FriendlyName) {
return adapter->FriendlyName;
}
}

return L"";
}
}

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "netapi32.lib")
Expand All @@ -20,7 +63,8 @@ bool DnsManager::SetDns(const std::string& interfaceGuid, const std::string& dns

// Try Windows SDK API first (Windows 10 1809+)
GUID guid;
std::wstring guidWide = WinUtils::Utf8ToWide(interfaceGuid);
const std::string normalizedGuid = NormalizeGuid(interfaceGuid);
std::wstring guidWide = WinUtils::Utf8ToWide(normalizedGuid);
HRESULT hr = CLSIDFromString(guidWide.c_str(), &guid);
if (SUCCEEDED(hr)) {
// Prepare DNS server addresses
Expand Down Expand Up @@ -54,8 +98,11 @@ bool DnsManager::SetDns(const std::string& interfaceGuid, const std::string& dns
}

// Fallback to netsh if SDK API fails or not available
std::wstring adapterName = FindAdapterFriendlyName(interfaceGuid);
std::wstring adapterSelector = adapterName.empty() ? guidWide : adapterName;

std::wstringstream cmd;
cmd << L"interface ip set dns \"" << guidWide << L"\" static " << WinUtils::Utf8ToWide(dns1);
cmd << L"interface ipv4 set dnsservers name=\"" << adapterSelector << L"\" static " << WinUtils::Utf8ToWide(dns1) << L" primary";

STARTUPINFOW si = {0};
si.cb = sizeof(si);
Expand All @@ -78,7 +125,7 @@ bool DnsManager::SetDns(const std::string& interfaceGuid, const std::string& dns
// Set secondary DNS if provided
if (!dns2.empty()) {
std::wstringstream cmd2;
cmd2 << L"interface ip add dns \"" << guidWide << L"\" " << WinUtils::Utf8ToWide(dns2) << L" index=2";
cmd2 << L"interface ipv4 add dnsservers name=\"" << adapterSelector << L"\" " << WinUtils::Utf8ToWide(dns2) << L" index=2";
std::wstring cmdLine2 = L"netsh.exe " + cmd2.str();
std::vector<WCHAR> cmdLineBuf2(cmdLine2.begin(), cmdLine2.end());
cmdLineBuf2.push_back(L'\0');
Expand Down Expand Up @@ -113,7 +160,8 @@ bool DnsManager::RestoreDefaultDns(const std::string& interfaceGuid) {

// Try Windows SDK API first
GUID guid;
std::wstring guidWide = WinUtils::Utf8ToWide(interfaceGuid);
const std::string normalizedGuid = NormalizeGuid(interfaceGuid);
std::wstring guidWide = WinUtils::Utf8ToWide(normalizedGuid);
HRESULT hr = CLSIDFromString(guidWide.c_str(), &guid);
if (SUCCEEDED(hr)) {
DNS_INTERFACE_SETTINGS settings = {0};
Expand All @@ -130,8 +178,11 @@ bool DnsManager::RestoreDefaultDns(const std::string& interfaceGuid) {
}

// Fallback to netsh
std::wstring adapterName = FindAdapterFriendlyName(interfaceGuid);
std::wstring adapterSelector = adapterName.empty() ? guidWide : adapterName;

std::wstringstream cmd;
cmd << L"interface ip set dns \"" << guidWide << L"\" dhcp";
cmd << L"interface ipv4 set dnsservers name=\"" << adapterSelector << L"\" source=dhcp";

STARTUPINFOW si = {0};
si.cb = sizeof(si);
Expand Down
4 changes: 2 additions & 2 deletions cpp_core/src/network_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ NetworkList NetworkDiscovery::DiscoverAdapters() {
}
}

// Only add enabled adapters
if (netAdapter.isEnabled) {
// Skip loopback adapters but keep disabled/disconnected adapters visible in UI.
if (adapter->IfType != IF_TYPE_SOFTWARE_LOOPBACK) {
list.adapters.push_back(netAdapter);
}

Expand Down
3 changes: 3 additions & 0 deletions windows_gui/DNSChanger.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<RootNamespace>DNSChanger</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<Version>2.1.0</Version>
<FileVersion>2.1.0.0</FileVersion>
<AssemblyVersion>2.1.0.0</AssemblyVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
58 changes: 41 additions & 17 deletions windows_gui/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,16 @@
BorderBrush="{StaticResource BorderBrush}"
BorderThickness="0,0,0,1">
<Grid>
<TextBlock Text="DNS Changer"
FontSize="24"
FontWeight="Bold"
Foreground="{StaticResource ForegroundPrimaryBrush}"
VerticalAlignment="Center"/>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Text="DNS Changer"
FontSize="24"
FontWeight="Bold"
Foreground="{StaticResource ForegroundPrimaryBrush}"/>
<TextBlock Text="v2.1.0"
FontSize="11"
Foreground="{StaticResource ForegroundSecondaryBrush}"
Margin="0,2,0,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{Binding StatusMessage}"
Foreground="{StaticResource ForegroundSecondaryBrush}"
Expand Down Expand Up @@ -75,15 +80,22 @@
Margin="0,0,12,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<TextBlock Grid.Row="0"
Text="Network Adapters"
Style="{StaticResource HeaderText}"/>

<TextBlock Grid.Row="1"
Text="Connected adapters are prioritized automatically."
Foreground="{StaticResource ForegroundSecondaryBrush}"
FontSize="11"
Margin="0,6,0,10"/>

<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<ListBox ItemsSource="{Binding Networks}"
SelectedItem="{Binding SelectedNetwork}"
Style="{StaticResource ModernListBox}"
Expand All @@ -108,21 +120,33 @@
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Opacity="0.6"
Margin="0,2,0,0"/>
<TextBlock Text="{Binding DnsPrimary}"
<TextBlock Text="{Binding DnsSummary}"
FontSize="11"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Opacity="0.6"
TextTrimming="CharacterEllipsis"
Margin="0,2,0,0"/>
<Border Background="{Binding IsConnected, Converter={StaticResource BoolToColorConverter}}"
CornerRadius="10"
Padding="6,2"
HorizontalAlignment="Left"
Margin="0,4,0,0">
<TextBlock Text="{Binding StatusBadge}"
FontSize="10"
Foreground="White"
FontWeight="SemiBold"/>
</Border>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<Border Background="{Binding IsConnected, Converter={StaticResource BoolToColorConverter}}"
CornerRadius="10"
Padding="6,2"
HorizontalAlignment="Left"
Margin="0,0,6,0">
<TextBlock Text="{Binding StatusBadge}"
FontSize="10"
Foreground="White"
FontWeight="SemiBold"/>
</Border>
<Border Background="#50545B"
CornerRadius="10"
Padding="6,2"
HorizontalAlignment="Left">
<TextBlock Text="{Binding AdapterState}"
FontSize="10"
Foreground="White"
FontWeight="SemiBold"/>
</Border>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Expand Down
4 changes: 3 additions & 1 deletion windows_gui/Models/NetworkModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ public class NetworkModel
public string DnsSecondary { get; set; } = string.Empty;
public bool IsEnabled { get; set; }
public bool IsConnected { get; set; }

public string StatusBadge => IsConnected ? "Connected" : "Disconnected";
public string AdapterState => IsEnabled ? "Enabled" : "Disabled";
public string DnsSummary => string.IsNullOrWhiteSpace(DnsSecondary) ? DnsPrimary : $"{DnsPrimary}, {DnsSecondary}";
}
}
29 changes: 26 additions & 3 deletions windows_gui/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,20 @@ private async Task RefreshNetworksAsync()
{
StatusMessage = "Refreshing networks...";
Networks = await _dnsService.GetNetworksAsync();
if (Networks.Count > 0 && SelectedNetwork == null)

if (Networks.Count > 0)
{
SelectedNetwork = Networks.FirstOrDefault(n => n.IsConnected)
?? Networks.FirstOrDefault(n => n.IsEnabled)
?? Networks[0];
}
else
{
SelectedNetwork = Networks[0];
SelectedNetwork = null;
}
StatusMessage = $"Found {Networks.Count} network(s)";

int connectedCount = Networks.Count(n => n.IsConnected);
StatusMessage = $"Found {Networks.Count} network(s), {connectedCount} connected";
}

private async Task RefreshDnsServersAsync()
Expand Down Expand Up @@ -349,6 +358,13 @@ public void ApplyDns()
if (SelectedNetwork == null || SelectedDns == null)
return;

if (!SelectedNetwork.IsEnabled)
{
UiService.ShowError($"{SelectedNetwork.Name} is disabled. Enable the adapter and try again.");
StatusMessage = "Selected adapter is disabled";
Comment on lines +361 to +364

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Distinguish disabled adapters from disconnected ones

This new guard blocks DNS changes for every network where IsEnabled is false, but in cpp_core/src/network_windows.cpp both isEnabled and isConnected are derived from adapter->OperStatus == IfOperStatusUp. In practice, enabled-but-disconnected adapters (for example an unplugged Ethernet NIC) report OperStatus down, so they are mislabeled as disabled and users are prevented from apply/restore operations even though the change is intended to block only truly disabled adapters.

Useful? React with 👍 / 👎.

return;
}

// Check admin rights before attempting
if (!AdminService.IsRunningAsAdministrator())
{
Expand Down Expand Up @@ -394,6 +410,13 @@ public void RestoreDefaultDns()
if (SelectedNetwork == null)
return;

if (!SelectedNetwork.IsEnabled)
{
UiService.ShowError($"{SelectedNetwork.Name} is disabled. Enable the adapter and try again.");
StatusMessage = "Selected adapter is disabled";
return;
}

// Check admin rights before attempting
if (!AdminService.IsRunningAsAdministrator())
{
Expand Down