functions/Test-TcpPorts.ps1
<#
.SYNOPSIS Tests connectivity to specified TCP ports on target hosts. .DESCRIPTION This function allows you to test connectivity to TCP ports for a list of target hosts. You can specify a single port, a range of ports, or use a list of 100 or 1000 most common TCP ports. The function can receive target inputs and port numbers via the pipeline and can also use the clipboard for target input. Results can be sorted or filtered to show only open ports. .PARAMETER Targets Target hosts (IP addresses or domain names) that need to be tested. This parameter can receive input from the pipeline. .PARAMETER UseClipboardInput If specified, it uses clipboard contents as target input. .PARAMETER PortNumber A single port number to test, validated to be in the range of 1 to 65535. .PARAMETER PortRange A range of ports to test, specified in "startPort-endPort" format, validated to ensure range is correct. .PARAMETER Timeout Timeout for connections, default is 1000 milliseconds. .PARAMETER UseCommon100Ports If specified, test against the 100 most common TCP ports. .PARAMETER UseCommon1000Ports If specified, test against the 1000 most common TCP ports. .PARAMETER SortResults If specified, results will be sorted by IP address. .PARAMETER MaxThreads Maximum number of concurrent threads. .PARAMETER FilePath File path for the ports database. Defaults to a CSV file named `ports.csv` in the script's directory. .EXAMPLE Test-TcpPorts -Targets '192.168.1.1' -PortNumber 80 Tests connectivity on port 80 for the IP address 192.168.1.1. .EXAMPLE '192.168.1.1', '192.168.1.2' | Test-TcpPorts -UseCommon100Ports Tests connectivity on the 100 most common TCP ports for the given IP addresses. .EXAMPLE Test-TcpPorts -Targets '192.168.1.1/24' -PortRange '80-85' -OnlyShowOpenPorts -SortResults Tests connectivity on ports 80 to 85 within the given subnet, sorts the results, and shows only open ports. .EXAMPLE Test-TcpPorts -UseClipboardInput -UseCommon1000Ports Uses clipboard contents as target IP addresses or hostnames and tests the most common 1000 TCP ports. .NOTES Ensure the port description database CSV file exists at the specified file path. #> function Test-TcpPorts { [CmdletBinding()] param ( [Parameter(ValueFromPipeline)] $Targets, [switch]$UseClipboardInput, [Parameter(ValueFromPipeline)] [ValidateRange(1, 65535)] [int]$PortNumber, [ValidateScript({ if ($_ -match '^(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])-(?:[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$') { $parts = $_ -split '-' $startPort = [int]$parts[0] $endPort = [int]$parts[1] if ($startPort -le $endPort) { $true } else { throw "Invalid port range. Start port must be less than or equal to end port." } } else { throw "Invalid input format. Please use the format 'startPort-endPort'." } })] $PortRange, [int]$timeout = 1000, [switch]$UseCommon100Ports, [switch]$UseCommon1000Ports, [switch]$SortResults, [switch]$ResolveDNS, [int]$MaxThreads = 100, [string]$filePath = "$PSScriptRoot\ports.csv" ) # Validate input parameters and port configurations if (-not $PortNumber -and -not $PortRange -and -not $UseCommon100Ports -and -not $UseCommon1000Ports) { Write-Host -ForegroundColor Red "Please specify a port number or port range using the -PortNumber or -PortRange parameter." return } # Import and filter TCP ports database if (-Not (Test-Path -Path $filePath)) { Write-Host -ForegroundColor Red "port description database CSV file not found at path: $filePath" return $null } else { $portsDB = Import-Csv -Path $filePath # Filter only TCP protocol entries $portsDB = $portsDB | Where-Object { $_.Protocol -eq "tcp" } } # Determine which ports to test based on input parameters $portsToTest = if ($PortNumber) { $PortNumber } elseif ($PortRange) { # Convert port range string to array of ports $PortRange.Split('-')[0]..$PortRange.Split('-')[1] } elseif ($UseCommon100Ports) { # Get first 100 most common ports ($portsDB | Select-Object -First 100).port } elseif ($UseCommon1000Ports) { # Get first 1000 most common ports ($portsDB | Select-Object -First 1000).port } # Handle clipboard input if specified if ($UseClipboardInput) { $Targets = Get-Clipboard } # Process target inputs based on their type switch ($Targets.GetType().Name) { "String" { # Handle IP range format (e.g., 192.168.1.1-192.168.1.254) if ($Targets -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") { $Targets = Get-IpAddressesInRange -Range $Targets } # Handle CIDR notation (e.g., 192.168.1.0/24) elseif ($Targets -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/([1-2][0-9]|3[0-2]|[0-9])") { $Targets = Get-IPAddressesInSubnet -Subnet $Targets } } "Object[]" { # Filter valid IP addresses and hostnames $Targets = $Targets -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$|^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$" # Sort IP addresses if requested and all targets are IPs if ($SortResults -and ($Targets -notmatch "^(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$").Count -eq $Targets.Count) { $Targets = Sort-IpAddress $Targets } elseif ($SortResults) { Write-Host -ForegroundColor Yellow "A mixed list of IP addresses and hostnames cannot be sorted!" } } Default { Throw "The [$Targets] is Invalid IPv4Address" } } # Resolve DNS names if requested if ($ResolveDNS) { for ($i = 0; $i -lt $Targets.Count; $i++) { if ($Targets[$i] -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") { $NameHost = (Resolve-DnsName $Targets[$i] -Type PTR -DnsOnly -ErrorAction SilentlyContinue).NameHost if ($NameHost) { if ($NameHost.Count -gt 1) { $ipList[$i] = $NameHost[0] } else { $Targets[$i] = $NameHost } } } } } # Initialize runspace pool for parallel processing $pool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads) $pool.Open() # Create array list for tracking runspaces $runspaces = New-Object System.Collections.ArrayList # Define the TCP port testing script block $scriptBlock = { param($hostname, $port, $timeout) function Test-TcpPortHelper { param ( $hostname, $port, $timeout = 1000 ) $objResult = [PSCustomObject]@{ Hostname = $hostname Port = $port Status = "Unknown" } try { $tcpClient = New-Object System.Net.Sockets.TcpClient $asyncResult = $tcpClient.BeginConnect($hostname, $port, $null, $null) $success = $asyncResult.AsyncWaitHandle.WaitOne($timeout, $false) if ($success) { $objResult.Status = "Open" } else { $objResult.Status = "Closed" } $tcpClient.Close() $tcpClient.Dispose() Return $objResult } catch { Write-Output "Error: Port $port is closed on $hostname." } } Return Test-TcpPortHelper -hostname $hostname -port $port -timeout $timeout } # Track progress variables $totalCount = $Targets.Count * $portsToTest.Count $completedCount = 0 # Create and start runspaces for each target/port combination foreach ($hostname in $Targets) { foreach ($port in $portsToTest) { $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($hostname).AddArgument($port).AddArgument($timeout) $powershell.RunspacePool = $pool $runspaces.Add([PSCustomObject]@{ Pipe = $powershell AsyncResult = $powershell.BeginInvoke() }) | Out-Null } } # Collect and process results $resultArray = New-Object System.Collections.ArrayList foreach ($runspace in $runspaces) { $result = $runspace.Pipe.EndInvoke($runspace.AsyncResult) $runspace.Pipe.Dispose() # Add results based on filter settings if ($result.Status -eq "Open") { $resultArray.Add(($result | Select-Object Hostname, @{Name = "Service"; Expression = { $portsDB | Where-Object { $_.port -eq $result.Port } | Select-Object -ExpandProperty Name } }, Port, Status)) | Out-Null } # Update progress bar $completedCount++ $percent = ($completedCount / $totalCount) * 100 Write-Progress -Activity "Testing TCP Ports" -Status "$completedCount out of $totalCount" -PercentComplete $percent } # Clean up resources $pool.Close() $pool.Dispose() if ($resultArray.Count -eq 0) { Write-Host -ForegroundColor Yellow "No open ports found." } else { return $resultArray } } |