Private/Test-VBPrivateIP.ps1
|
# ============================================================ # FUNCTION : Test-VBPrivateIP # VERSION : 1.0.0 # CHANGED : 2026-05-07 -- Initial build # AUTHOR : Vibhu Bhatnagar # PURPOSE : Classify an IP address as private/reserved or public # ENCODING : UTF-8 with BOM # ============================================================ <# .SYNOPSIS Returns $true if the supplied IP address falls within a private or reserved range. .DESCRIPTION Checks an IP address string against RFC1918 private IPv4 ranges, loopback, link-local, and IPv6 private/local ranges. Used by Invoke-VBDNSLogParser via the IP classification cache — this function is called only once per unique IP address, not once per log line. IPv4 ranges checked: 10.0.0.0/8 RFC1918 172.16.0.0/12 RFC1918 192.168.0.0/16 RFC1918 127.0.0.0/8 Loopback 169.254.0.0/16 Link-local (APIPA) IPv6 ranges checked: ::1 Loopback fe80::/10 Link-local fc00::/7 Unique local (ULA) .PARAMETER IPAddress The IP address string to classify. Accepts both IPv4 and IPv6. .OUTPUTS [bool] $true if private/reserved, $false if public. .NOTES Version : 1.0.0 Author : Vibhu Bhatnagar Modified : 2026-05-07 Category : Private Called by: Invoke-VBDNSLogParser (once per unique IP via cache) Regex patterns are compiled at module load time (script-scope) and reused. Do not move pattern compilation inside the function body. #> # Module-level compiled regex constants — compiled once at dot-source time, reused per call $script:_PrivateIPv4Regex = [regex]::new( '^(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|' + '172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|' + '192\.168\.\d{1,3}\.\d{1,3}|' + '127\.\d{1,3}\.\d{1,3}\.\d{1,3}|' + '169\.254\.\d{1,3}\.\d{1,3})$', [System.Text.RegularExpressions.RegexOptions]::Compiled ) function Test-VBPrivateIP { [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory = $true)] [string]$IPAddress ) if ([string]::IsNullOrWhiteSpace($IPAddress)) { return $false } # Step 1 -- IPv4 classification via compiled regex if ($IPAddress -match '^\d') { return $script:_PrivateIPv4Regex.IsMatch($IPAddress) } # Step 2 -- IPv6 classification via StartsWith (faster than regex for prefix checks) $ipLower = $IPAddress.ToLower() # Loopback if ($ipLower -eq '::1') { return $true } # Link-local fe80::/10 — covers fe80 through febf if ($ipLower.StartsWith('fe80:') -or $ipLower.StartsWith('fe81:') -or $ipLower.StartsWith('fe82:') -or $ipLower.StartsWith('fe83:') -or $ipLower.StartsWith('fe84:') -or $ipLower.StartsWith('fe85:') -or $ipLower.StartsWith('fe86:') -or $ipLower.StartsWith('fe87:') -or $ipLower.StartsWith('fe88:') -or $ipLower.StartsWith('fe89:') -or $ipLower.StartsWith('fe8a:') -or $ipLower.StartsWith('fe8b:') -or $ipLower.StartsWith('fe8c:') -or $ipLower.StartsWith('fe8d:') -or $ipLower.StartsWith('fe8e:') -or $ipLower.StartsWith('fe8f:') -or $ipLower.StartsWith('fe90:') -or $ipLower.StartsWith('fe91:') -or $ipLower.StartsWith('fe92:') -or $ipLower.StartsWith('fe93:') -or $ipLower.StartsWith('fe94:') -or $ipLower.StartsWith('fe95:') -or $ipLower.StartsWith('fe96:') -or $ipLower.StartsWith('fe97:') -or $ipLower.StartsWith('fe98:') -or $ipLower.StartsWith('fe99:') -or $ipLower.StartsWith('fe9a:') -or $ipLower.StartsWith('fe9b:') -or $ipLower.StartsWith('fe9c:') -or $ipLower.StartsWith('fe9d:') -or $ipLower.StartsWith('fe9e:') -or $ipLower.StartsWith('fe9f:') -or $ipLower.StartsWith('fea0:') -or $ipLower.StartsWith('fea1:') -or $ipLower.StartsWith('fea2:') -or $ipLower.StartsWith('fea3:') -or $ipLower.StartsWith('fea4:') -or $ipLower.StartsWith('fea5:') -or $ipLower.StartsWith('fea6:') -or $ipLower.StartsWith('fea7:') -or $ipLower.StartsWith('fea8:') -or $ipLower.StartsWith('fea9:') -or $ipLower.StartsWith('feaa:') -or $ipLower.StartsWith('feab:') -or $ipLower.StartsWith('feac:') -or $ipLower.StartsWith('fead:') -or $ipLower.StartsWith('feae:') -or $ipLower.StartsWith('feaf:') -or $ipLower.StartsWith('feb0:') -or $ipLower.StartsWith('feb1:') -or $ipLower.StartsWith('feb2:') -or $ipLower.StartsWith('feb3:') -or $ipLower.StartsWith('feb4:') -or $ipLower.StartsWith('feb5:') -or $ipLower.StartsWith('feb6:') -or $ipLower.StartsWith('feb7:') -or $ipLower.StartsWith('feb8:') -or $ipLower.StartsWith('feb9:') -or $ipLower.StartsWith('feba:') -or $ipLower.StartsWith('febb:') -or $ipLower.StartsWith('febc:') -or $ipLower.StartsWith('febd:') -or $ipLower.StartsWith('febe:') -or $ipLower.StartsWith('febf:')) { return $true } # Unique local fc00::/7 — covers fc00 through fdff if ($ipLower.StartsWith('fc') -or $ipLower.StartsWith('fd')) { return $true } return $false } |