397 lines
9.3 KiB
PowerShell
397 lines
9.3 KiB
PowerShell
#
|
|
# ConnectivityCheck.ps1
|
|
#
|
|
param (
|
|
[parameter(Mandatory=$true)]
|
|
[string]
|
|
$NameResolutionFileName,
|
|
|
|
[parameter(Mandatory=$true)]
|
|
[string]
|
|
$TcpEndpointsFileName,
|
|
|
|
[parameter(Mandatory=$false)]
|
|
[string]
|
|
$OutDir = $Env:Temp,
|
|
|
|
[parameter(Mandatory=$false)]
|
|
[string]
|
|
$CheckHostsFile
|
|
)
|
|
|
|
$BlnCheckHostsFile = $false
|
|
if ($CheckHostsFile) {
|
|
if ($CheckHostsFile -ine "false" -and $CheckHostFile -ine "no" -and $CheckHostFile -ne "0") {
|
|
$BlnCheckHostsFile = $true
|
|
}
|
|
}
|
|
|
|
# Functions
|
|
#
|
|
|
|
# Override the built-in cmdlet with a custom version
|
|
function Write-ErrorMsg($Message) {
|
|
[Console]::ForegroundColor = 'red'
|
|
Write-Output $Message
|
|
[Console]::ResetColor()
|
|
}
|
|
|
|
function Convert-ByteArrayToHex {
|
|
[cmdletbinding()]
|
|
param(
|
|
[parameter(Mandatory=$true)]
|
|
[Byte[]]
|
|
$Bytes
|
|
)
|
|
#$HexString = [System.Text.StringBuilder]::new($Bytes.Length * 2)
|
|
$HexString = New-Object System.Text.StringBuilder($Bytes.Length * 2)
|
|
ForEach($Byte in $Bytes){
|
|
$HexString.AppendFormat("{0:x2}", $Byte) | Out-Null
|
|
}
|
|
$HexString.ToString()
|
|
}
|
|
|
|
function Convert-HexToByteArray {
|
|
[cmdletbinding()]
|
|
param(
|
|
[parameter(Mandatory=$true)]
|
|
[String]
|
|
$HexString)
|
|
# $Bytes = [byte[]]::new($HexString.Length / 2)
|
|
$Bytes = New-Object byte[]($HexString.Length / 2)
|
|
For($i=0; $i -lt $HexString.Length; $i+=2){
|
|
$Bytes[$i/2] = [convert]::ToByte($HexString.Substring($i, 2), 16)
|
|
}
|
|
$Bytes
|
|
}
|
|
|
|
function IsPortOpen {
|
|
param(
|
|
[parameter(Mandatory=$true)]
|
|
[string]
|
|
$HostName,
|
|
[parameter(Mandatory=$true)]
|
|
[int]
|
|
$Port,
|
|
[parameter(Mandatory=$false)]
|
|
[int]
|
|
$Timeout = 5000)
|
|
|
|
$Result = $null
|
|
|
|
# REMOVED: TCPClient's ConnectAsync not available in older powershell
|
|
# $Client = New-Object System.Net.Sockets.TCPClient
|
|
# $Timeout =
|
|
# $IsOpen = $Client.ConnectAsync($HostName, $Port).Wait($Timeout)
|
|
|
|
$Client = New-Object System.Net.Sockets.TCPClient
|
|
$IsOpen = $false
|
|
try {
|
|
$AsyncResult = $Client.BeginConnect($HostName, $Port, $null, $null)
|
|
if (!$AsyncResult.AsyncWaitHandle.WaitOne($Timeout, $false))
|
|
{
|
|
throw [System.TimeoutException]::new();
|
|
}
|
|
$Client.EndConnect($AsyncResult) | Out-Null
|
|
$IsOpen = $Client.Connected
|
|
}
|
|
catch {
|
|
$IsOpen = $false
|
|
}
|
|
finally {
|
|
$Client.Close();
|
|
}
|
|
|
|
if ($IsOpen) {
|
|
$Result = "Success"
|
|
}
|
|
else {
|
|
$Result = "Fail"
|
|
}
|
|
"$($Result): $($HostName):$($Port)"
|
|
}
|
|
|
|
function Read-Hosts-File-To-Hash {
|
|
Param (
|
|
[string] $FileName,
|
|
[int] $IncludeCommentedLines)
|
|
|
|
$HostHash = @{}
|
|
if (-not (Test-Path -Path $FileName)) {
|
|
Write-ErrorMsg "Error: file not found: $($FileName)"
|
|
return $null
|
|
}
|
|
# $Lines = Get-Content -Path $FileName
|
|
$Lines = [IO.File]::ReadAllLines($FileName)
|
|
foreach ($Line in $Lines) {
|
|
$Line = $Line.Trim()
|
|
if ($Line.Length -lt 9) {
|
|
continue
|
|
}
|
|
$Chars = $Line.ToCharArray()
|
|
if ($Chars[0] -eq '#') {
|
|
if ($IncludeCommentedLines -eq 0) {
|
|
continue
|
|
}
|
|
}
|
|
$Line = $Line.Trim('#')
|
|
if ($Line.Length -lt 9) {
|
|
continue
|
|
}
|
|
$ix = $Line.IndexOf('#')
|
|
if ($ix -gt 0) {
|
|
if ($ix -lt 9) {
|
|
continue
|
|
}
|
|
$Line = $Line.Substring(0, $ix)
|
|
}
|
|
$Words = ($Line -split ' ')
|
|
if ($Words.Count -lt 2) {
|
|
continue
|
|
}
|
|
$Ip = $Words[0]
|
|
try {
|
|
[IPAddress]$Ip > $null
|
|
}
|
|
catch {
|
|
continue
|
|
}
|
|
$Names = New-Object System.Collections.Generic.List[String]
|
|
for ($i = 1; $i -lt $Words.Count; ++$i) {
|
|
$Word = $Words[$i].Trim()
|
|
if ($Word) {
|
|
$Names.Add($Words[$i])
|
|
}
|
|
}
|
|
if ($HostHash.ContainsKey($Ip)) {
|
|
foreach ($Name in $Names) {
|
|
$HostHash[$Ip].Add($Name)
|
|
}
|
|
}
|
|
else {
|
|
$HostHash.Add($Ip, $Names)
|
|
}
|
|
}
|
|
$HostHash
|
|
}
|
|
|
|
function Read-TcpEndpoints-File-To-Hash {
|
|
Param (
|
|
[string] $FileName,
|
|
[int] $IncludeCommentedLines)
|
|
|
|
$TcpEndpointsHash = @{}
|
|
if (-not (Test-Path -Path $FileName)) {
|
|
Write-ErrorMsg "Error: file not found: $($FileName)"
|
|
return $null
|
|
}
|
|
# $Lines = Get-Content -Path $FileName
|
|
$Lines = [IO.File]::ReadAllLines($FileName)
|
|
foreach ($Line in $Lines) {
|
|
$Line = $Line.Trim()
|
|
if ($Line.Length -lt 9) {
|
|
continue
|
|
}
|
|
$Chars = $Line.ToCharArray()
|
|
if ($Chars[0] -eq '#') {
|
|
if ($IncludeCommentedLines -eq 0) {
|
|
continue
|
|
}
|
|
}
|
|
$Line = $Line.Trim('#')
|
|
if ($Line.Length -lt 9) {
|
|
continue
|
|
}
|
|
$ix = $Line.IndexOf('#')
|
|
if ($ix -gt 0) {
|
|
if ($ix -lt 9) {
|
|
continue
|
|
}
|
|
$Line = $Line.Substring(0, $ix)
|
|
}
|
|
$Parts = ($Line -split ' ')
|
|
if ($Parts.Count -lt 2) {
|
|
continue
|
|
}
|
|
$Ip = $Parts[0]
|
|
try {
|
|
[IPAddress]$Ip > $null
|
|
}
|
|
catch {
|
|
continue
|
|
}
|
|
$PortsCsv = $Parts[1]
|
|
$PortsArr = ($PortsCsv -split ',')
|
|
$Ports = New-Object System.Collections.Generic.List[String]
|
|
for ($i = 0; $i -lt $PortsArr.Count; ++$i) {
|
|
$Port = $PortsArr[$i]
|
|
$Port = $Port.Trim()
|
|
if ($Port) {
|
|
$Ports.Add($Port)
|
|
}
|
|
}
|
|
if ($TcpEndpointsHash.ContainsKey($Ip)) {
|
|
foreach ($Port in $Ports) {
|
|
$TcpEndpointsHash[$Ip] = $Ports
|
|
}
|
|
}
|
|
else {
|
|
$TcpEndpointsHash.Add($Ip, $Ports)
|
|
}
|
|
}
|
|
$TcpEndpointsHash
|
|
}
|
|
|
|
|
|
# Set Up
|
|
#
|
|
|
|
$DateStringPrefix = Get-Date -Format "yyyy-MM-dd_HHmmss__"
|
|
if (-not (Test-Path -Path $OutDir)) {
|
|
New-Item -ItemType Directory -Force -Path $OutDir >$null
|
|
}
|
|
$OutFileName = Join-Path -Path $OutDir -ChildPath "$($DateStringPrefix)ConnectivityCheck.log"
|
|
$ErrorActionPreference="SilentlyContinue"
|
|
Stop-Transcript | out-null
|
|
$ErrorActionPreference = "Continue"
|
|
Start-Transcript -path $OutFileName
|
|
Write-Host
|
|
|
|
# Retrieve IPs and host names from file for name resolution check.
|
|
$HostsFileName = "$($Env:windir)\system32\drivers\etc\hosts"
|
|
if (Test-Path -Path $NameResolutionFileName) {
|
|
Write-Host "Retrieving IPs and host names from $NameResolutionFileName..."
|
|
$NameResolutionTable = Read-Hosts-File-To-Hash $NameResolutionFileName 0
|
|
} else {
|
|
Write-ErrorMsg "Name resolution file missing: $NameResolutionFileName"
|
|
$NameResolutionTable = $null
|
|
}
|
|
if (Test-Path -Path $HostsFileName) {
|
|
$NameResolutionTable = Read-Hosts-File-To-Hash $HostsFileName.ToString() 0
|
|
} else {
|
|
Write-ErrorMsg "System hosts file missing: $HostsFileName"
|
|
$NameResolutionTable = $null
|
|
}
|
|
|
|
# Retrieve TCP endpoints from file for connectivity check.
|
|
if (Test-Path -Path $TcpEndpointsFileName) {
|
|
Write-Host "Retrieving TCP endpoints from $TcpEndpointsFileName..."
|
|
$TcpEndpointMap = Read-TcpEndpoints-File-To-Hash $TcpEndpointsFileName
|
|
} else {
|
|
Write-ErrorMsg "TCP endpoints file missing: $TcpEndpointsFileName"
|
|
$TcpEndpointMap = $null
|
|
}
|
|
|
|
|
|
# Name Resolution Check
|
|
#
|
|
|
|
Write-Output "`r`n`r`nNAME RESOLUTION TEST ($NameResolutionFileName):`r`n"
|
|
if ($null -ne $NameResolutionTable) {
|
|
foreach ($IpCorrect in $NameResolutionTable.Keys) {
|
|
$Names = $NameResolutionTable[$IpCorrect]
|
|
foreach ($Name in $Names) {
|
|
if (!$Name) {
|
|
continue
|
|
}
|
|
$AddressList = $null
|
|
try {
|
|
$AddressList = [System.Net.Dns]::GetHostAddresses($Name)
|
|
}
|
|
catch {
|
|
Write-ErrorMsg "Error: $($Name) doesn't resolve."
|
|
continue
|
|
}
|
|
$IpList = New-Object 'System.Collections.Generic.List[string]'
|
|
foreach ($Ip in $AddressList) {
|
|
if ($Ip.AddressFamily -ne "InterNetwork") {
|
|
continue
|
|
}
|
|
if ($IpList.Contains($Ip.IPAddressToString)) {
|
|
continue
|
|
}
|
|
$IpList.Add($Ip.IPAddressToString) > $null
|
|
}
|
|
if ($IpList.Count -eq 0) {
|
|
Write-ErrorMsg "Error: $($Name) doesn't resolve to any IPv4 addresses."
|
|
continue
|
|
}
|
|
if ($IpList.Count -gt 1) {
|
|
Write-ErrorMsg "Error: $($Name) resolves to multiple IP addresses: $($IpList -join ', ')"
|
|
continue
|
|
}
|
|
$Ip = $IpList[0]
|
|
if ($IpCorrect -eq $Ip) {
|
|
#Write-Output "Success: $($Name) resolves to $($IpCorrect)"
|
|
Write-Output "Success: $($IpCorrect) resolved from $($Name)"
|
|
}
|
|
else {
|
|
Write-ErrorMsg "Error: $($Name) resolves to $($Ip) instead of $($IpCorrect)."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Hosts file check
|
|
#
|
|
|
|
if ($BlnCheckHostsFile) {
|
|
Write-Output "`r`n`r`nHOSTS FILE CHECK ($($HostsFileName)):`r`n"
|
|
if ($null -ne $NameResolutionTable -and $null -ne $NameResolutionTable) {
|
|
foreach ($Ip in $NameResolutionTable.Keys) {
|
|
$SourceNames = $NameResolutionTable[$Ip]
|
|
$SourceNamesStr = $SourceNames -join " "
|
|
$SourceEntryStr = "$($Ip) $($SourceNamesStr)"
|
|
if (-not $NameResolutionTable.ContainsKey($Ip)) {
|
|
Write-ErrorMsg "Error: missing entry equivalent to '$($SourceEntryStr)'"
|
|
continue
|
|
}
|
|
|
|
$MissingNames = @()
|
|
$Names = $NameResolutionTable[$Ip]
|
|
foreach ($SourceName in $SourceNames) {
|
|
if (-not ($Names -contains $SourceName)) {
|
|
$MissingNames += $SourceName
|
|
}
|
|
}
|
|
if (0 -lt $MissingNames.count) {
|
|
Write-ErrorMsg "Error: entry or entries for $($Ip) missing the following names: $($MissingNames -join ' ')"
|
|
} else {
|
|
foreach ($SourceName in $SourceNames) {
|
|
Write-Output "Success: found $($Ip) $($SourceName)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# TCP connectivity check
|
|
#
|
|
|
|
$EndPoints = New-Object System.Collections.Generic.List[Object]
|
|
Write-Output "`r`n`r`nTCP CONNECTIVITY TEST:`r`n"
|
|
if ($null -ne $TcpEndpointMap) {
|
|
foreach ($Ip in $TcpEndpointMap.Keys)
|
|
{
|
|
$Ports = $TcpEndpointMap[$Ip]
|
|
foreach ($Port in $Ports) {
|
|
$ThisEndPoint = New-Object PSObject -Property @{
|
|
Host = [string]$Ip
|
|
Port = [int]$Port
|
|
}
|
|
$EndPoints.Add($ThisEndPoint)
|
|
}
|
|
}
|
|
}
|
|
foreach ($EndPoint in $EndPoints) {
|
|
IsPortOpen -hostName $EndPoint.Host -port $EndPoint.Port
|
|
}
|
|
|
|
|
|
# Clean up
|
|
#
|
|
|
|
Stop-Transcript
|