Set-DynamicIPDoHServer.psm1
|
# test if the session has administrator privileges # https://devblogs.microsoft.com/scripting/check-for-admin-credentials-in-a-powershell-script/ If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` [Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Warning "You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!" Break } # main function function Set-DynamicIPDoHServer { [Alias("set-ddoh")] param ($DoHTemplate, $DoHDomain) # check if DohTemplate and DohDomain are not empty if ((-NOT ($dohTemplate)) -or (-NOT ($DoHDomain))) { write-host "DoH template and DoH domain are both required" -ForegroundColor red Break } # DoH template must start with "HTTPS:// and needs a / after the TLD. the Add-DnsClientDohServerAddress cmdlet will fail if there is no / after the TLD" if ($dohTemplate -notmatch '^https\:\/\/.+\..+\/.*') { write-host "DNS over HTTPS (DoH) template starts with HTTPS:// and needs a / after the TLD" -ForegroundColor Magenta Break } # DoH domain must have a proper TLD if ($DohDomain -notmatch '^.+\..+') { write-host "DoH Domain isn't right" -ForegroundColor Magenta Break } # error handling for the entire function - to make sure there is no error before attempting to create the scheduled task try { # get the currently active network interface/adapter that is being used for Internet access # This gets the correct network adapter if no VPN or Hyper-V virtual switch is being used $ActiveNetworkInterface = Get-NetRoute -DestinationPrefix '0.0.0.0/0', '::/0' | Sort-Object -Property { $_.InterfaceMetric + $_.RouteMetric } -Top 1 -PipelineVariable ActiveAdapter | Get-NetAdapter | Where-Object {$_.ifIndex -eq $ActiveAdapter.ifIndex} $ActiveNetworkInterface.InterfaceDescription # checks if the detected active interface from the previous step is virtual, if it is, checks if it's an external virtual Hyper-V network adapter or VPN virtual network adapter if ((Get-NetAdapter | Where-Object { $_.InterfaceGuid -eq $ActiveNetworkInterface.InterfaceGuid}).Virtual) { Write-Host "Interface is virtual, trying to find out if it's a VPN virtual adapter or Hyper-V External virtual switch" -ForegroundColor Magenta # if it's an external virtual Hyper-V network adapter, it must be the correct adapter if ($ActiveNetworkInterface.InterfaceDescription -like "*Hyper-V Virtual Ethernet Adapter*" ) { Write-Host "The detected active network adapter is virtual but that's OK because it's Hyper-V External switch" -ForegroundColor Magenta $ActiveNetworkInterface = $ActiveNetworkInterface } # if the detected active network adapter is virtual and not virtual external Hyper-V network adapter, which means it is VPN virtual network adapter, choose the second prioritized adapter/interface based on route metric # tested with Cloudflare WARP, Wintun, OpenVPN and has been always successful in detecting the correct network adapter/interface else { write-host "Detected active network adapter is virtual but not virtual Hyper-V adapter, most likely a VPN virtual network adapter, choosing the second prioritized adapter/interface based on route metric" -ForegroundColor Yellow $ActiveNetworkInterface = Get-NetRoute -DestinationPrefix '0.0.0.0/0', '::/0' | Sort-Object -Property { $_.InterfaceMetric + $_.RouteMetric } -Top 2 -PipelineVariable ActiveAdapter | Get-NetAdapter | Where-Object {$_.ifIndex -eq $ActiveAdapter.ifIndex} } } write-host "This is the final detected network adapter this module is going to set Secure DNS for" -ForegroundColor Magenta $ActiveNetworkInterface # luckily, it's not normally possible to change description of network interfaces/adapters # so it is a solid criteria for choosing our network adapter/interface # https://serverfault.com/questions/862065/changing-nic-interface-descriptions-in-windows#:~:text=You%20can%27t%20change%20the%20name%20of%20the%20NICs,you%27ll%20have%20to%20do%20lots%20of%20name%20swapping # check if there is any IP address already associated with "$DoHTemplate" template $oldIPs = (Get-DnsClientDohServerAddress | Where-Object {$_.dohTemplate -eq $DoHTemplate}).serveraddress # if there is, remove them if ($oldIPs) { $oldIPs | ForEach-Object { remove-DnsClientDohServerAddress -ServerAddress $_ } } # reset the network adapter's DNS servers back to default to take care of any IPv6 strays Set-DnsClientServerAddress -InterfaceIndex $ActiveNetworkInterface.ifIndex -ResetServerAddresses -ErrorAction Stop # only uncomment for debugging purposes # Write-Host "info about the selected network interface/adapter" -ForegroundColor Magenta # $ActiveNetworkInterface.Name # $ActiveNetworkInterface.InterfaceGuid # $ActiveNetworkInterface.ifIndex # delete all other previous DoH settings for ALL Interface - Windows behavior in settings when changing DoH settings is to delete all DoH settings for the interface we are modifying # but we need to delete all DoH settings for ALL interfaces in here because every time we virtualize a network adapter with external switch of Hyper-V, # Hyper-V assigns a new GUID to it, so it's better not to leave any leftover in the registry and clean up after ourselves remove-item "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\*" -Recurse # get the new IPv4s for $DoHDomain try { Write-Host "Using System DNS to get IPv4s for $DoHDomain" -ForegroundColor Magenta; $NewIPsV4 = (Resolve-DnsName -Name $DoHDomain -Type A -ErrorAction Stop).ipaddress } catch { Write-Host "System DNS failed, using 1.1.1.1 from Cloudflare to to get IPv4s for $DoHDomain" -ForegroundColor Magenta; $NewIPsV4 = (Resolve-DnsName -Name $DoHDomain -Server 1.1.1.1 -Type A).ipaddress } # loop through each IPv4 $NewIPsV4 | foreach-Object { # defining registry path for DoH settings of the $ActiveNetworkInterface based on its GUID for IPv4 $Path = "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\$($ActiveNetworkInterface.InterfaceGuid)\DohInterfaceSettings\Doh\$_" # associating the new IPv4s with our DoH template in Windows DoH template predefined list Add-DnsClientDohServerAddress -ServerAddress $_ -DohTemplate $DoHTemplate -AllowFallbackToUdp $False -AutoUpgrade $True # add DoH settings for the specified Network adapter based on its GUID in registry # value 1 for DohFlags key means use automatic template for DoH, 2 means manual template, since we add our template to Windows, it's predefined so we use value 1 New-Item -Path $Path -Force | Out-Null New-ItemProperty -Path $Path -Name "DohFlags" -Value 1 -PropertyType Qword -Force } # get the new IPv6s for $DoHDomain try { Write-Host "Using System DNS to get IPv6s for $DoHDomain" -ForegroundColor Magenta; $NewIPsV6 = (Resolve-DnsName -Name $DoHDomain -Type AAAA -ErrorAction Stop).ipaddress } catch { Write-Host "System DNS failed, using 1.1.1.1 from Cloudflare to get IPv6s for $DoHDomain" -ForegroundColor Magenta; $NewIPsV6 = (Resolve-DnsName -Name $DoHDomain -Server 1.1.1.1 -Type AAAA).ipaddress } # loop through each IPv6 $NewIPsV6 | foreach-Object { # defining registry path for DoH settings of the $ActiveNetworkInterface based on its GUID for IPv6 $Path = "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\$($ActiveNetworkInterface.InterfaceGuid)\DohInterfaceSettings\Doh6\$_" # associating the new IPv6s with our DoH template in Windows DoH template predefined list Add-DnsClientDohServerAddress -ServerAddress $_ -DohTemplate $DoHTemplate -AllowFallbackToUdp $False -AutoUpgrade $True # add DoH settings for the specified Network adapter based on its GUID in registry # value 1 for DohFlags key means use automatic template for DoH, 2 means manual template, since we already added our template to Windows, it's considered predefined, so we use value 1 New-Item -Path $Path -Force | Out-Null New-ItemProperty -Path $Path -Name "DohFlags" -Value 1 -PropertyType Qword -Force } # gather IPv4s and IPv6s all in one place $NewIPs = $NewIPsV4 + $NewIPsV6 # $NewIPs = $NewIPs -join ',' # apparently that wasn't needed and it already works # this is responsible for making the changes in Windows settings UI > Network and internet > $ActiveNetworkInterface.Name Set-DnsClientServerAddress -ServerAddresses $NewIPs -InterfaceIndex $ActiveNetworkInterface.ifIndex -ErrorAction Stop # clear DNS client Cache Clear-DnsClientCache } catch { write-host "these errors occured after running the module" -ForegroundColor white $_ $ModuleErrors = $_ } # for debugging purposes, uncomment this to get more info about the event # Get-WinEvent -ListLog Microsoft-Windows-DNS-Client/Operational | Format-List is* # here we enable logging for the event log below (which is disabled by default) and set its log size from the default 1MB to 2MB $logName = 'Microsoft-Windows-DNS-Client/Operational' $log = New-Object System.Diagnostics.Eventing.Reader.EventLogConfiguration $logName $log.MaximumSizeInBytes=2048000 $log.IsEnabled=$true $log.SaveChanges() if (!$ModuleErrors) { write-host "No errors occured when running the module, creating the scheduled task now if it's not already been created" -ForegroundColor green # create a scheduled task if (-NOT (Get-ScheduledTask -TaskName "Dynamic DoH Server IP check" -ErrorAction SilentlyContinue)) { $action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-executionPolicy bypass -command `"set-ddoh -DoHTemplate '$DoHTemplate' -DoHDomain '$DoHDomain'`"" $TaskPrincipal = New-ScheduledTaskPrincipal -LogonType S4U -UserId $env:USERNAME -RunLevel Highest # trigger 1 $CIMTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger $EventTrigger = New-CimInstance -CimClass $CIMTriggerClass -ClientOnly $EventTrigger.Subscription = @" <QueryList><Query Id="0" Path="Microsoft-Windows-DNS-Client/Operational"><Select Path="Microsoft-Windows-DNS-Client/Operational">*[System[Provider[@Name='Microsoft-Windows-DNS-Client'] and EventID=1013]]</Select></Query></QueryList> "@ $EventTrigger.Enabled = $True $EventTrigger.ExecutionTimeLimit = "PT1M" # trigger 2 $Time = New-ScheduledTaskTrigger ` -Once -At (Get-Date).AddHours(1) ` -RandomDelay (New-TimeSpan -Seconds 30) ` -RepetitionInterval (New-TimeSpan -Hours 2) ` # register the task Register-ScheduledTask -Action $action -Trigger $EventTrigger,$Time -Principal $TaskPrincipal -TaskPath "DDoH" -TaskName "Dynamic DoH Server IP check" -Description "Checks for New IPs of our Dynamic DoH server" # define advanced settings for the task $TaskSettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -Compatibility Win8 -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 1) # add advanced settings we defined to the task Set-ScheduledTask -TaskPath "DDoH" -TaskName "Dynamic DoH Server IP check" -Settings $TaskSettings } } } # end of main function |