Private/Resolve-AACHostName.ps1
|
function Resolve-AACHostName { <# .SYNOPSIS Looks up many host names in DNS at once, and says which exist. .DESCRIPTION For dangling redirect URIs: a host that no longer resolves (NXDOMAIN) - an app service, storage account or Front Door deleted while an app still sends tokens to it - can be claimed by anyone. Returns a hashtable: host -> $true (it resolves), $false (no such host), $null (unknown: the lookup timed out or failed otherwise). All lookups run together, -TimeoutSeconds in all. #> [CmdletBinding()] [OutputType([hashtable])] param( [AllowEmptyCollection()] [string[]] $HostName = @(), [ValidateRange(1, 120)] [int] $TimeoutSeconds = 10 ) $result = @{} $names = @($HostName | Where-Object { $_ } | ForEach-Object { $_.ToLowerInvariant() } | Select-Object -Unique) if (-not $names.Count) { return $result } $tasks = @{} foreach ($name in $names) { try { $tasks[$name] = [System.Net.Dns]::GetHostAddressesAsync($name) } catch { $result[$name] = $null } } try { $null = [System.Threading.Tasks.Task]::WaitAll([System.Threading.Tasks.Task[]]@($tasks.Values), [TimeSpan]::FromSeconds($TimeoutSeconds)) } catch { Write-Verbose "Some DNS lookups failed: $($_.Exception.Message)" } foreach ($name in $tasks.Keys) { $task = $tasks[$name] $result[$name] = if ($task.IsCompletedSuccessfully) { [bool]@($task.Result).Count } elseif ($task.IsFaulted -and @($task.Exception.InnerExceptions | Where-Object { $_ -is [System.Net.Sockets.SocketException] -and $_.SocketErrorCode -in 'HostNotFound', 'NoData' }).Count) { $false } else { $null } } $result } |