Files
winutil/functions/private/Set-WinUtilDNS.ps1
T
KEERTHIVASAN KandChris Titus 8e3998d9fd feat(dns): Add real-time DNS speed benchmark and 'Fastest' auto-selection option (#4935)
* feat(dns): Add real-time DNS speed benchmark and 'Fastest' auto-selection option

* fix(dns): Address CodeRabbit & Codex PR review feedback

* fix(ci): Remove .biomeignore to keep repository root clean

* Fix Fastest DNS eligibility and failed probe handling

---------

Co-authored-by: Chris Titus <contact@christitus.com>
2026-09-19 18:16:16 -05:00

144 lines
8.0 KiB
PowerShell

function Set-WinUtilDNS {
<#
.SYNOPSIS
Sets the DNS of all interfaces that are in the "Up" state. It will lookup the values from the DNS.Json file
.PARAMETER DNSProvider
The DNS provider to set the DNS server to
.EXAMPLE
Set-WinUtilDNS -DNSProvider "google"
#>
param($DNSProvider)
if($DNSProvider -eq "Default") {
Write-WinUtilLog -Component "DNS" -Message "DNS provider is Default; no DNS changes applied."
return $true
}
if($DNSProvider -eq "Fastest") {
Write-WinUtilLog -Component "DNS" -Message "Auto-detecting fastest DNS provider via latency benchmark..."
$benchmark = Get-WinUtilDNSBenchmark
$validFastest = $benchmark | Where-Object { $_.LatencyMs -lt 9999 } | Select-Object -First 1
if ($validFastest) {
$DNSProvider = $validFastest.Provider
Write-Host "Auto-selected fastest DNS provider: $DNSProvider ($($validFastest.LatencyMs) ms)"
Write-WinUtilLog -Component "DNS" -Message "Auto-selected fastest DNS provider: $DNSProvider ($($validFastest.LatencyMs) ms)"
} else {
Write-Warning "Could not measure DNS latency to any provider; keeping current network adapter DNS settings."
Write-WinUtilLog -Component "DNS" -Message "Benchmark timeout or all probes failed; aborting DNS change to preserve existing settings."
return $false
}
}
try {
$Adapters = Get-NetAdapter | Where-Object {$_.Status -eq "Up"}
Write-Host "Ensuring DNS is set to $DNSProvider on the following interfaces:"
Write-Host $($Adapters | Out-String)
Write-WinUtilLog -Component "DNS" -Message "Setting DNS provider to $DNSProvider for $(@($Adapters).Count) active adapter(s)."
if($DNSProvider -ne "DHCP") {
$dns = $sync.configs.dns.$DNSProvider
if($null -eq $dns) {
Write-Warning "DNS provider $DNSProvider was not found in configuration."
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not found in configuration."
return $false
}
}
$dohSupported = [bool](Get-Command Add-DnsClientDohServerAddress -ErrorAction SilentlyContinue)
if ($DNSProvider -ne "DHCP" -and $dns.DohOnly -and -not $dohSupported) {
Write-Warning "DNS provider $DNSProvider requires DNS over HTTPS, which is not supported on this system."
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider requires DNS over HTTPS, which is not supported on this system."
return $false
}
$dnscacheBase = "HKLM:\System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters"
Foreach ($Adapter in $Adapters) {
$interfaceParams = "$dnscacheBase\$($Adapter.InterfaceGuid)"
if($DNSProvider -eq "DHCP") {
Write-WinUtilLog -Component "DNS" -Message "Resetting DNS to DHCP on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex))."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ResetServerAddresses
netsh interface ip set dnsservers name="$($Adapter.Name)" source=dhcp
netsh interface ipv6 set dnsservers name="$($Adapter.Name)" source=dhcp
$dohInterfaceSettings = "$interfaceParams\DohInterfaceSettings"
if (Test-Path $dohInterfaceSettings) {
if ($dohSupported) {
$dohServerAddresses = @(
Get-ChildItem -Path "$dohInterfaceSettings\Doh" -ErrorAction SilentlyContinue
Get-ChildItem -Path "$dohInterfaceSettings\Doh6" -ErrorAction SilentlyContinue
) | Select-Object -ExpandProperty PSChildName -Unique
foreach ($ip in $dohServerAddresses) {
if (Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue) {
Write-WinUtilLog -Component "DNS" -Message "Removing DoH registration for $ip."
Remove-DnsClientDohServerAddress -ServerAddress $ip -Confirm:$false -ErrorAction Stop
}
}
}
Remove-Item -Path $dohInterfaceSettings -Recurse -Force -ErrorAction SilentlyContinue
}
} else {
$ipv4Addresses = @(@($dns.Primary, $dns.Secondary) | Where-Object { $_ })
$ipv6Addresses = @(@($dns.Primary6, $dns.Secondary6) | Where-Object { $_ })
if ($dohSupported -and $dns.DohTemplate) {
try {
$ips = @($dns.Primary, $dns.Secondary, $dns.Primary6, $dns.Secondary6) | Where-Object { $_ }
foreach ($ip in $ips) {
$dohTemplate = if ($dns.SecondaryDohTemplate -and @($dns.Secondary, $dns.Secondary6) -contains $ip) {
$dns.SecondaryDohTemplate
} else {
$dns.DohTemplate
}
$existing = Get-DnsClientDohServerAddress -ServerAddress $ip -ErrorAction SilentlyContinue
if ($existing) {
Set-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
} else {
Write-WinUtilLog -Component "DNS" -Message "Registering DoH template for $ip."
Add-DnsClientDohServerAddress -ServerAddress $ip -DohTemplate $dohTemplate -AllowFallbackToUdp $false -AutoUpgrade $true -ErrorAction Stop
}
$leaf = if ($ip.Contains(':')) { 'Doh6' } else { 'Doh' }
$regPath = "$interfaceParams\DohInterfaceSettings\$leaf\$ip"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force -ErrorAction Stop | Out-Null
}
New-ItemProperty -Path $regPath -Name "DohFlags" -Value 1 -PropertyType QWord -Force -ErrorAction Stop | Out-Null
}
} catch {
if ($dns.DohOnly) {
throw
}
Write-Warning "DNS over HTTPS setup for provider $DNSProvider failed; continuing with plain DNS."
Write-WinUtilLog -Level "WARN" -Component "DNS" -Message "DNS over HTTPS setup for provider $DNSProvider failed; continuing with plain DNS: $($psitem.Exception.Message)"
}
}
Write-WinUtilLog -Component "DNS" -Message "Setting IPv4 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary), $($dns.Secondary)."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses $ipv4Addresses -ErrorAction Stop
Write-WinUtilLog -Component "DNS" -Message "Setting IPv6 DNS on adapter $($Adapter.Name) (ifIndex: $($Adapter.ifIndex)) to $($dns.Primary6), $($dns.Secondary6)."
Set-DnsClientServerAddress -InterfaceIndex $Adapter.ifIndex -ServerAddresses $ipv6Addresses -ErrorAction Stop
}
}
if ($DNSProvider -ne "DHCP" -and $dohSupported -and $dns.DohTemplate) {
Clear-DnsClientCache
}
Write-WinUtilLog -Component "DNS" -Message "DNS provider change completed: $DNSProvider"
return $true
} catch {
Write-Warning "DNS provider $DNSProvider was not completed because an error occurred."
Write-Warning $psitem.Exception.Message
Write-WinUtilLog -Level "ERROR" -Component "DNS" -Message "DNS provider $DNSProvider was not completed: $($psitem.Exception.Message)"
return $false
}
}