|
| 1 | +function CountAvailableEphemeralPorts([string]$protocol = "TCP") { |
| 2 | + |
| 3 | + [uint32]$portRangeSize = 64 |
| 4 | + # First, remove all the text bells and whistle (plain text, table headers, dashes, empty lines, ...) from netsh output |
| 5 | + $tcpRanges = (netsh int ipv4 sh excludedportrange $protocol) -replace "[^0-9,\ ]", '' | ? { $_.trim() -ne "" } |
| 6 | + |
| 7 | + # Then, remove any extra space characters. Only capture the numbers representing the beginning and end of range |
| 8 | + $tcpRangesArray = $tcpRanges -replace "\s+(\d+)\s+(\d+)\s+", '$1,$2' | ConvertFrom-String -Delimiter "," |
| 9 | + #Convert from PSCustomObject to Object[] type |
| 10 | + $tcpRangesArray = @($tcpRangesArray) |
| 11 | + |
| 12 | + # Extract the ephemeral ports ranges |
| 13 | + $EphemeralPortRange = (netsh int ipv4 sh dynamicportrange $protocol) -replace "[^0-9]", '' | ? { $_.trim() -ne "" } |
| 14 | + $EphemeralPortStart = [Convert]::ToUInt32($EphemeralPortRange[0]) |
| 15 | + $EphemeralPortEnd = $EphemeralPortStart + [Convert]::ToUInt32($EphemeralPortRange[1]) - 1 |
| 16 | + |
| 17 | + # Find the external interface |
| 18 | + $externalInterfaceIdx = (Get-NetRoute -DestinationPrefix "0.0.0.0/0")[0].InterfaceIndex |
| 19 | + $hostIP = (Get-NetIPConfiguration -ifIndex $externalInterfaceIdx).IPv4Address.IPAddress |
| 20 | + |
| 21 | + # Extract the used TCP ports from the external interface |
| 22 | + $usedTcpPorts = (Get-NetTCPConnection -LocalAddress $hostIP -ErrorAction Ignore).LocalPort |
| 23 | + $usedTcpPorts | % { $tcpRangesArray += [pscustomobject]@{P1 = $_; P2 = $_ } } |
| 24 | + |
| 25 | + # Extract the used TCP ports from the 0.0.0.0 interface |
| 26 | + $usedTcpGlobalPorts = (Get-NetTCPConnection -LocalAddress "0.0.0.0" -ErrorAction Ignore).LocalPort |
| 27 | + $usedTcpGlobalPorts | % { $tcpRangesArray += [pscustomobject]@{P1 = $_; P2 = $_ } } |
| 28 | + # Sort the list and remove duplicates |
| 29 | + $tcpRangesArray = ($tcpRangesArray | Sort-Object { $_.P1 } -Unique) |
| 30 | + |
| 31 | + $tcpRangesList = New-Object System.Collections.ArrayList($null) |
| 32 | + $tcpRangesList.AddRange($tcpRangesArray) |
| 33 | + |
| 34 | + # Remove overlapping ranges |
| 35 | + for ($i = $tcpRangesList.P1.Length - 2; $i -gt 0 ; $i--) { |
| 36 | + if ($tcpRangesList[$i].P2 -gt $tcpRangesList[$i + 1].P1 ) { |
| 37 | + $tcpRangesList.Remove($tcpRangesList[$i + 1]) |
| 38 | + $i++ |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + # Remove the non-ephemeral port reservations from the list |
| 43 | + $filteredTcpRangeArray = $tcpRangesList | ? { $_.P1 -ge $EphemeralPortStart } |
| 44 | + $filteredTcpRangeArray = $filteredTcpRangeArray | ? { $_.P2 -le $EphemeralPortEnd } |
| 45 | + |
| 46 | + if ($null -eq $filteredTcpRangeArray) { |
| 47 | + $freeRanges = @($EphemeralPortRange[1]) |
| 48 | + } |
| 49 | + else { |
| 50 | + $freeRanges = @() |
| 51 | + # The first free range goes from $EphemeralPortStart to the beginning of the first reserved range |
| 52 | + $freeRanges += ([Convert]::ToUInt32($filteredTcpRangeArray[0].P1) - $EphemeralPortStart) |
| 53 | + |
| 54 | + for ($i = 1; $i -lt $filteredTcpRangeArray.length; $i++) { |
| 55 | + # Subsequent free ranges go from the end of the previous reserved range to the beginning of the current reserved range |
| 56 | + $freeRanges += ([Convert]::ToUInt32($filteredTcpRangeArray[$i].P1) - [Convert]::ToUInt32($filteredTcpRangeArray[$i - 1].P2) - 1) |
| 57 | + } |
| 58 | + |
| 59 | + # The last free range goes from the end of the last reserved range to $EphemeralPortEnd |
| 60 | + $freeRanges += ($EphemeralPortEnd - [Convert]::ToUInt32($filteredTcpRangeArray[$filteredTcpRangeArray.length - 1].P2)) |
| 61 | + } |
| 62 | + |
| 63 | + # Count the number of available free ranges |
| 64 | + [uint32]$freeRangesCount = 0 |
| 65 | + ($freeRanges | % { $freeRangesCount += [Math]::Floor($_ / $portRangeSize) } ) |
| 66 | + |
| 67 | + return $freeRangesCount |
| 68 | +} |
| 69 | + |
| 70 | +function CheckPortExhaustion { |
| 71 | + Write-Host "Checking Port Exhaustion" |
| 72 | + $avTcpPorts = CountAvailableEphemeralPorts -protocol TCP |
| 73 | + if($avTcpPorts -lt 10) { |
| 74 | + Write-Host "Available TCP ports are $avTcpPorts. Port exhaustion suspected." -ForegroundColor Red |
| 75 | + return $true |
| 76 | + } |
| 77 | + $avUdpPorts = CountAvailableEphemeralPorts -protocol UDP |
| 78 | + if($avTcpPorts -lt 10) { |
| 79 | + Write-Host "Available UDP ports are $avUdpPorts. Port exhaustion suspected." -ForegroundColor Red |
| 80 | + return $true |
| 81 | + } |
| 82 | + Write-Host "Available TCP Ports : $avTcpPorts , UDP Ports : $avUdpPorts . No port exhaustion suspected." -ForegroundColor Green |
| 83 | + return $false |
| 84 | +} |
| 85 | + |
| 86 | +Write-Host "Total wait time : 100 seconds..." |
| 87 | + |
| 88 | +for($i = 1; $i -le 10; $i++) { |
| 89 | + Write-Host "Iteration : $i" |
| 90 | + if(CheckPortExhaustion) { |
| 91 | + Write-Host "DNS Issue Found." -ForegroundColor Red |
| 92 | + return |
| 93 | + } |
| 94 | + Start-Sleep -Seconds 10 |
| 95 | +} |
0 commit comments