IsPortActive.ps1

<#PSScriptInfo
.VERSION 1.0.0
 
.GUID 7ec65ff2-79a3-4ff1-be3c-2dc6b6a3a3d7
 
.AUTHOR asheroto
 
.COMPANYNAME asheroto
 
.TAGS PowerShell Windows port active network check program listening
 
.RELEASENOTES
[Version 0.0.1] - Initial Release.
[Version 0.0.2] - Removed function.
[Version 0.0.3] - Improved description.
[Version 0.0.4] - Fix example.
[Version 0.0.5] - Added code signing cert.
[Version 1.0.0] - Totally refactored script. Added support for UDP ports. Added support for checking by process name and process ID. Added support for returning service/display name if it's svchost. Added -Help, -CheckForUpdate, and -Version.
#>


<#
.SYNOPSIS
    Checks if a port is currently active or in use.
.DESCRIPTION
    This script checks if a port is currently active or in use. It checks for active TCP and UDP connections on a specified port, by process name, or by a process ID.
 
    Here are key switches to guide the script's behavior:
      -Port to specify the port number to check for active connections.
      -ProcessName to specify the name of the process to check for active connections.
      -ProcessId to specify the process ID to check for active connections.
 
    Additional utilities include:
      -Version switch displays the current version of the script.
      -Help switch brings up the help information for the script usage.
      -CheckForUpdate switch verifies if the current script version is up-to-date.
.PARAMETER Port
    Specifies the port number to check for active connections.
.PARAMETER ProcessName
    Specifies the name of the process to check for active connections.
.PARAMETER ProcessId
    Specifies the process ID to check for active connections.
.PARAMETER Version
    Displays the version of the script.
.PARAMETER Help
    Displays the help information for the script.
.PARAMETER CheckForUpdate
    Checks for updates of the script.
.EXAMPLE
    # Check if port 443 is active
    IsPortActive -Port 443
.EXAMPLE
    # Check if port 80 is active using process name
    IsPortActive -ProcessName "httpd"
.EXAMPLE
    # Check if port 8080 is active using process ID
    IsPortActive -ProcessId 1234
.NOTES
    Version : 1.0.0
    Created by : asheroto
#>


#Requires -RunAsAdministrator

[CmdletBinding()]
param (
    [Int32]$Port,
    [String]$ProcessName,
    [Int32]$ProcessId,
    [switch]$Version,
    [switch]$Help,
    [switch]$CheckForUpdate
)

# Version
$CurrentVersion = '1.0.0'
$RepoOwner = 'asheroto'
$RepoName = 'IsPortActive'

# Make sure that -Port, -ProcessName, or -ProcessId are not specified together
if (($Port -and $ProcessName) -or ($Port -and $ProcessId) -or ($ProcessName -and $ProcessId)) {
    Write-Error "You can only specify one of the following parameters: -Port, -ProcessName, or -ProcessId"
    exit 1
}

# Check if -Version is specified
if ($Version.IsPresent) {
    Write-Output "$RepoName $CurrentVersion"
    exit 0
}

# Line separator
function LineSeparator {
    Return ("─" * 80)
}

# Help
if ($Help) {
    Get-Help -Name $MyInvocation.MyCommand.Source -Full
    exit 0
}

# Check the GitHub release for the latest version
function Check-GitHubRelease {
    param (
        [string]$Owner,
        [string]$Repo
    )
    try {
        $url = "https://api.github.com/repos/$Owner/$Repo/releases/latest"
        $response = Invoke-RestMethod -Uri $url -ErrorAction Stop

        $latestVersion = $response.tag_name
        $publishedAt = $response.published_at
        $UtcDateTimeFormat = "MM/dd/yyyy HH:mm:ss"

        # Convert UTC time string to local time
        $UtcDateTime = [DateTime]::ParseExact($publishedAt, $UtcDateTimeFormat, $null)
        $PublishedLocalDateTime = $UtcDateTime.ToLocalTime()

        [PSCustomObject]@{
            LatestVersion     = $latestVersion
            PublishedDateTime = $PublishedLocalDateTime
        }
    } catch {
        Write-Error "Unable to check for updates. Error: $_"
        exit 1
    }
}

# Check for updates
if ($CheckForUpdate) {
    $Data = Check-GitHubRelease -Owner $RepoOwner -Repo $RepoName

    if ($Data.LatestVersion -gt $CurrentVersion) {
        Write-Warning "$RepoName is out of date.`n"
        Write-Output "Current version: $CurrentVersion. Latest version: $($Data.LatestVersion). Published at: $($Data.PublishedDateTime).`n"
        Write-Output "You can download the latest version from https://github.com/$RepoOwner/$RepoName/releases`n"
        Write-Output "If you have PowerShell 5 or later, you can run"
        Write-Output "`tInstall-Script -Name $RepoName -Force"
        Write-Output "to install the latest version.`n"
    } else {
        Write-Output "`n$RepoName is up to date.`n"
        Write-Output "Current version: $CurrentVersion. Latest version: $($Data.LatestVersion). Published at: $($Data.PublishedDateTime).`n"
        Write-Output "Repository: https://github.com/$RepoOwner/$RepoName/releases`n"
        Write-Output "PowerShell Gallery: https://www.powershellgallery.com/packages/$RepoName"
    }
    exit 0
}
function Get-Connections {
    param (
        [int]$ProcessId,
        [string]$ProcessName,
        [int]$Port
    )

    $tcpConnections = Get-NetTCPConnection
    $udpEndpoints = Get-NetUDPEndpoint

    if ($ProcessId) {
        $tcpConnections = $tcpConnections | Where-Object { $_.OwningProcess -eq $ProcessId }
        $udpEndpoints = $udpEndpoints | Where-Object { $_.OwningProcess -eq $ProcessId }
    }

    if ($ProcessName) {
        $processes = Get-Process | Where-Object { $_.Name -eq $ProcessName } -ErrorAction SilentlyContinue
        $processIds = $processes.Id

        $tcpConnections = $tcpConnections | Where-Object { $processIds -contains $_.OwningProcess }
        $udpEndpoints = $udpEndpoints | Where-Object { $processIds -contains $_.OwningProcess }
    }

    if ($Port) {
        $tcpConnections = $tcpConnections | Where-Object { $_.LocalPort -eq $Port }
        $udpEndpoints = $udpEndpoints | Where-Object { $_.LocalPort -eq $Port }
    }

    $Connections = @{}

    # Get all services
    $services = Get-WmiObject -Query "SELECT * FROM Win32_Service" -ErrorAction SilentlyContinue

    # Create a hashtable of service objects
    $serviceHashtable = @{}
    foreach ($service in $services) {
        $serviceHashtable[$service.ProcessID] = $service
    }

    $tcpConnections | Group-Object -Property LocalAddress | ForEach-Object {
        $localAddress = $_.Name
        if (-not $Connections.ContainsKey($localAddress)) {
            $Connections[$localAddress] = @()
        }
        $_.Group | ForEach-Object {
            $OwningProcess = $_.OwningProcess
            $ProcessName = (Get-Process -Id $OwningProcess -ErrorAction SilentlyContinue).Name
            $Path = (Get-Process -Id $OwningProcess -ErrorAction SilentlyContinue).Path
            $service = $serviceHashtable[$OwningProcess]

            if ($ProcessName -eq 'svchost' -and $service) {
                $Connection = [PSCustomObject][ordered]@{
                    "Service Name"   = $service.Name
                    "Display Name"   = $service.DisplayName
                    "Process Name"   = $ProcessName
                    "Process ID"     = $OwningProcess
                    "Path"           = $Path
                    "Local Address"  = $_.LocalAddress
                    "Local Port"     = $_.LocalPort
                    "Remote Address" = $_.RemoteAddress
                    "Remote Port"    = $_.RemotePort
                    "State"          = $_.State
                    "Protocol"       = "TCP"
                }
            } else {
                $Connection = [PSCustomObject][ordered]@{
                    "Process Name"   = $ProcessName
                    "Process ID"     = $OwningProcess
                    "Path"           = $Path
                    "Local Address"  = $_.LocalAddress
                    "Local Port"     = $_.LocalPort
                    "Remote Address" = $_.RemoteAddress
                    "Remote Port"    = $_.RemotePort
                    "State"          = $_.State
                    "Protocol"       = "TCP"
                }
            }

            $Connections[$localAddress] += $Connection
        }
    }

    $udpEndpoints | Group-Object -Property LocalAddress | ForEach-Object {
        $localAddress = $_.Name
        if (-not $Connections.ContainsKey($localAddress)) {
            $Connections[$localAddress] = @()
        }
        $_.Group | ForEach-Object {
            $OwningProcess = $_.OwningProcess
            $ProcessName = (Get-Process -Id $OwningProcess -ErrorAction SilentlyContinue).Name
            $Path = (Get-Process -Id $OwningProcess -ErrorAction SilentlyContinue).Path
            $service = $serviceHashtable[$OwningProcess]

            if ($ProcessName -eq 'svchost' -and $service) {
                $Connection = [PSCustomObject][ordered]@{
                    "Service Name"  = $service.Name
                    "Display Name"  = $service.DisplayName
                    "Process Name"  = $ProcessName
                    "Process ID"    = $OwningProcess
                    "Path"          = $Path
                    "Local Address" = $_.LocalAddress
                    "Local Port"    = $_.LocalPort
                    "Protocol"      = "UDP"
                }
            } else {
                $Connection = [PSCustomObject][ordered]@{
                    "Process Name"  = $ProcessName
                    "Process ID"    = $OwningProcess
                    "Path"          = $Path
                    "Local Address" = $_.LocalAddress
                    "Local Port"    = $_.LocalPort
                    "Protocol"      = "UDP"
                }
            }

            $Connections[$localAddress] += $Connection
        }
    }

    return $Connections
}

# Update note
Write-Output "$RepoName $CurrentVersion"
Write-Output "To check for updates, run $RepoName -CheckForUpdate`n"

# Validate parameters
if ($Port -gt 0 -and -not [string]::IsNullOrWhiteSpace($ProcessName)) {
    Write-Error "You must specify either -Port or -ProcessName, but not both."
    Write-Output ""
    exit 1
}

# ProcessId is specified, ignore other parameters
if ($ProcessId -gt 0) {
    $Connections = Get-Connections -ProcessId $ProcessId
} else {
    # Port or ProcessName is specified
    if ($Port -gt 0) {
        $Connections = Get-Connections -Port $Port
    } elseif (-not [string]::IsNullOrWhiteSpace($ProcessName)) {
        $Connections = Get-Connections -ProcessName $ProcessName
    } else {
        Write-Error "You must specify a port number or provide a process name or process ID."
        Write-Output ""
        exit 1
    }
}

# Output connections grouped by local address
$hasActiveConnections = $false

foreach ($localAddress in $Connections.Keys) {
    $localConnections = $Connections[$localAddress]

    if ($localConnections.Count -gt 0) {
        $hasActiveConnections = $true
        LineSeparator
        Write-Output "Local Address: $localAddress"
        LineSeparator

        $localConnections | ForEach-Object {
            $_ | Write-Output
        }
    }
}

if (-not $hasActiveConnections) {
    Write-Output "No active connections found."
}
# SIG # Begin signature block
# MIIpMAYJKoZIhvcNAQcCoIIpITCCKR0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwzYah17/8aTyU
# Dkq/2JvI7K1OhmC6c2nn2vCW6NlOGKCCDh8wggawMIIEmKADAgECAhAIrUCyYNKc
# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z
# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0
# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr
# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF
# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F
# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh
# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ
# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay
# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI
# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp
# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro
# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB
# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+
# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED
# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql
# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF
# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h
# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw
# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld
# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw
# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP
# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE
# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn
# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji
# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq
# yK+p/pQd52MbOoZWeE4wggdnMIIFT6ADAgECAhAN0Uk2zX4f3m8X7HdlwxBNMA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjMwMzE3MDAwMDAwWhcNMjQwMzE2
# MjM1OTU5WjBvMQswCQYDVQQGEwJVUzERMA8GA1UECBMIT2tsYWhvbWExETAPBgNV
# BAcTCE11c2tvZ2VlMRwwGgYDVQQKExNBc2hlciBTb2x1dGlvbnMgSW5jMRwwGgYD
# VQQDExNBc2hlciBTb2x1dGlvbnMgSW5jMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
# MIICCgKCAgEA081RwO7808Fuab0RP0L2gthlZB8fiiGUBpnqJhsD1Bzpk+45B2LA
# qmrUp+nZIXNwr5me/55enGI9CkhaxmZoFhBxoM1u5lODNp8GaAYzIEi0IJldzZ9y
# PAQMfhTkHRiOwKBqTGO3h/gSZtaZ+8F+ltCmlXvv2vpqFpt5JL+uJm9SRIN5WLiP
# QM/isjYR+eIcaZxQeHLfbnemNcaT4cXOMChUsmG6WsoHZO1o76dCN+owz23koLy2
# Y1R3N2PMQj3kj8Bnlph6ffNnitKhXuwj3NkWwPSSQvYhcBuTcCOxpXpUjWlQNuTt
# llTHp9leKMq11raPkSaLe2qVX4eBc6HPtBT+7XagpaA409d7fmYTOLKmE0BCEdgb
# YZzYmKSyjrAgWlU9SYxurhFgHuQFD0CsBW1aXl6IEjn26cVx+hmj2KCOFELAdh1r
# 9UTNt37a/o/TYCp/mQ22/oa/224is1dpNj7RAHnNaix5n8RKKHufEh85lVjS/cBn
# 7z3cCKejyfBaUGK10SUwZKJiJ51DKkRkdh4A5cL85wKkQcFnRpfT/T+KTOEYRFT/
# vz3uK9bMLwuBj+gkP3WnlVXf67IY3FfZaQUDNdtwur4UTGrDQOn8Xl2rEy7L9VlJ
# UOCjX93WfW0B1Q4IxSdF6vIJw1m44HpIU4jxnqTBEo6BVVRCtdmp/x0CAwEAAaOC
# AgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQW
# BBTH3/U7rGshoJKjtOAVqNAEWJ/PBDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
# CgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB
# Mzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEu
# Y3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93
# d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p
# bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG9w0B
# AQsFAAOCAgEAQtDUmTp7UG2w4A4WaT6BoMLBLqzm09S64nFfuIUFjWk3KTCStpwR
# 3KzwG78CpYb7I0G6T7O2Emv+u0WgKVaWPbLFlnrjXXB+68DxR+CFWh6UDioz/9wo
# +eD/V2eKilAc2WSEIC8NzXT3C4yEtxUmnebK7Ysxy4qLlb4Sxk9NspS+Lg3jKBxb
# ExduQWHi1ytqw9NCghzK1Y2h5/AHwSYfwz7AyRerN3gTwzmmgTaWYEHVCL0NQddO
# 1lkSz6LPq2/JWHns7I0tNPCT5nZYva1v34EZvP9+P+SUDBH8bfrm6HlTd+Z6qNW5
# ACsALaCCAsZRQ6i7UZfjolD/lADn65f46XfnNMIo8PPpagFBIvxg03DGDJQu4QnY
# AyZhtrLDxc8VLtGZP8QVBf9JVcjVD8FxMMobDnuDq0YZ1h3ydRo1dqOzWVDipp0i
# oPd0UbL7EcZr6QcM72LWFvAACyVcIiXlh5jY+JehqaZMlS9aw4WQT0gpvBOaOJqb
# vGoAbtyHRFIkFbJG/Wxkpr+VkU1JvilXCh8g0OsXwvJk4dK4GeBVa7VLlq95fLiK
# zL54EZDTY1W+YfKYUseiptRlu5XBUn15C9rTpqDZhHFz6exyLfYcJzdxJJdArjio
# UKKR9ZhLfxm1bmFMb8NPWOKH/ZI6vR0jNgwalk3nTx63ZnVAOJLH+BkxghpnMIIa
# YwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFB
# MD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5
# NiBTSEEzODQgMjAyMSBDQTECEA3RSTbNfh/ebxfsd2XDEE0wDQYJYIZIAWUDBAIB
# BQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB
# BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg
# buHeEl5CqOtMx/2hK5th3WmyepMnB5xde/LROaQRyK8wDQYJKoZIhvcNAQEBBQAE
# ggIAvLWUC/yZYBoStmZFQ4T7oqhO0aBhAYhrDkPcfY+fCud2GUCZx5EneGg3qgBX
# 8Pt/zXNg/y4fUv58/T5lIFLuwOBVYg9gKJ1YuOhFxU6y9Olm2bVPgIDqFYtzNK2i
# iqwmBXIaLsEBX5a1mwNUHiMklpqxifTiDU7i+6KX/VLf0ocfirvsYhtWRp41lkql
# h6X+H7RJU3QolbUuHjvSW81Y2PJcw0N5/nKI1hPvLbiJVSjbv3Xkq2tKHtchF5PI
# AhODvOpiPuliCglbKon9HfTf8yMe2DQ8gIx5wBk3exRF4Mw953EGubYtDdzxGGOo
# OhzOKEZ1avqlrSKJ0w0cIyKthZZDSW9aBytQ/NEK+t+o1p7VV++GvHIWU7c8495O
# gfxC90pzrbZI5pi78FNSEEJHcl5qFoXL+ZQfB6awY/mVacDcD2aweSO+QGX/zjOi
# a/D6J8b9Z1CY7h+CyrXS8J/j8UERVk+dsPVINPSTdORdLtl4XPViFZJwWXzyYAnC
# 3ykipzDeFUmmGAmAAkJ3wgNKTtaWFauC7lQcFZiPVYx8VsRE80QQNd2roa4U9Ftp
# WqYP6fsroxYm+/2ES3Ly7Wq2+p902+DEyHYZQPHdHEw5L7tzS9v2uFWtUPVphfDT
# GKDQl2RD1yQGSuXBNxyxkiNwpRH31yDXj7HnQVS6SUqRn9mhghc9MIIXOQYKKwYB
# BAGCNwMDATGCFykwghclBgkqhkiG9w0BBwKgghcWMIIXEgIBAzEPMA0GCWCGSAFl
# AwQCAQUAMHcGCyqGSIb3DQEJEAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglg
# hkgBZQMEAgEFAAQgY0iT7gh+BVGGWEBAjQBRSRPEGqJJbwS6N6OqlqzwFyQCEB8/
# pDHoZoh9a9KUGMQcduoYDzIwMjMwNjI0MDY0OTM5WqCCEwcwggbAMIIEqKADAgEC
# AhAMTWlyS5T6PCpKPSkHgD1aMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwOTIx
# MDAwMDAwWhcNMzMxMTIxMjM1OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMI
# RGlnaUNlcnQxJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26Ho
# FIV0MxomrNAcVR4eNm28klUMYfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6
# Va8z7GGK6aYo25BjXL2JU+A6LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hp
# DIjG8b7gL307scpTjUCDHufLckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/t
# jdRNlYRo44DLannR0hCRRinrPibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqia
# nJVHhoV5PgxeZowaCiS+nKrSnLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhM
# kRCWfk3h3cKtpX74LRsf7CtGGKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/
# NurlfDHn88JSxOYWe1p+pSVz28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M5
# 0GT6Vs/g9ArmFG1keLuY/ZTDcyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTA
# WOXlLimQprdhZPrZIGwYUWC6poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs
# 4Xu5zGcTB5rBeO3GiMiwbjJ5xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkD
# Tvpd6kVzHIR+187i1Dp3AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxq
# II+eyG8wHQYDVR0OBBYEFGKK3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEw
# T6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGD
# MIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYB
# BQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAFWqKhrzRvN4Vzcw/HXjT9aFI/H8+ZU5myXm93KKmMN31GT8Ffs2wklR
# LHiIY1UJRjkA/GnUypsp+6M/wMkAmxMdsJiJ3HjyzXyFzVOdr2LiYWajFCpFh0qY
# QitQ/Bu1nggwCfrkLdcJiXn5CeaIzn0buGqim8FTYAnoo7id160fHLjsmEHw9g6A
# ++T/350Qp+sAul9Kjxo6UrTqvwlJFTU2WZoPVNKyG39+XgmtdlSKdG3K0gVnK3br
# /5iyJpU4GYhEFOUKWaJr5yI+RCHSPxzAm+18SLLYkgyRTzxmlK9dAlPrnuKe5NMf
# hgFknADC6Vp0dQ094XmIvxwBl8kZI4DXNlpflhaxYwzGRkA7zl011Fk+Q5oYrsPJ
# y8P7mxNfarXH4PMFw1nfJ2Ir3kHJU7n/NBBn9iYymHv+XEKUgZSCnawKi8ZLFUrT
# mJBFYDOA4CPe+AOk9kVH5c64A0JH6EE2cXet/aLol3ROLtoeHYxayB6a1cLwxiKo
# T5u92ByaUcQvmvZfpyeXupYuhVfAYOd4Vn9q78KVmksRAsiCnMkaBXy6cbVOepls
# 9Oie1FqYyJ+/jbsYXEP10Cro4mLueATbvdH7WwqocH7wl4R44wgDXUcsY6glOJcB
# 0j862uXl9uab3H4szP8XTE0AotjWAQ64i+7m4HJViSwnGWH2dwGMMIIGrjCCBJag
# AwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIw
# MzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQg
# UlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCw
# zIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFz
# sbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ
# 7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7
# QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/teP
# c5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCY
# OjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9K
# oRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6
# dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM
# 1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbC
# dLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbEC
# AwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1N
# hS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAI
# BgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7Zv
# mKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI
# 2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/ty
# dBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVP
# ulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmB
# o1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc
# 6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3c
# HXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0d
# KNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZP
# J/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLe
# Mt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDy
# Divl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n
# 79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UE
# AxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoX
# DTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQw
# H/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6
# dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXG
# XuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXn
# Mcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy
# 19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFY
# F/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+Skjqe
# PdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFg
# qrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJR
# R3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7Gr
# hotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2
# MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9P
# MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIB
# hjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
# ZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRo
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu
# Y3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV
# 5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8t
# tzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKh
# SLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO
# 7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WY
# IsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3
# AamfV6peKOK5lDGCA3YwggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
# DkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJT
# QTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQDE1pckuU+jwqSj0pB4A9WjAN
# BglghkgBZQMEAgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJ
# KoZIhvcNAQkFMQ8XDTIzMDYyNDA2NDkzOVowKwYLKoZIhvcNAQkQAgwxHDAaMBgw
# FgQU84ciTYYzgpI1qZS8vY+W6f4cfHMwLwYJKoZIhvcNAQkEMSIEIILbVXqasZIl
# AP0i+zDAgcHV7azKdjwT3mQfWW/BJfdBMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIE
# IMf04b4yKIkgq+ImOr4axPxP5ngcLWTQTIB1V6Ajtbb6MA0GCSqGSIb3DQEBAQUA
# BIICAGUNSkr/39uuYvnfJRAOwKdQTmtZEloOHo63qLQW8Xw7FSCOpRi9pYdyj0o9
# wxiKX8rIW2UeMopks3gk7pUEZ8qq3YH7pZsFPUgCSDs+bG0ErSp7ojhJmV2mmQXQ
# J1ToPvMT75V6x6KInAH/xbMnxaWMSxTC5puFO6rMfsu5L/Coh5CxZnlm8AzKBCFr
# eAPbiRuKEC2WVq2At2CrPco7l4KCt2uMNhYDw27wISg7HomdLmL/KfdeQI224n+i
# iQOTzQu9XUg+6+s+SsUKl6LNRsSw42+EUzx78QAeh5WlxWsp/YB0OJUn14w16ZOR
# rN4qntempfKa14nJCyZqC/FDObY8EnfXF07Z+JtJ/0f9JSa+xAwfMfQhfAvUI+tt
# 4PosnVZDwm43+WILwE7glu0dsc2a6IYRGURxzCsuaCk4kuOkgrKaE4JHm35pPK/F
# HNcFHYp5aqOZc/yeWHIcznSxe83Vw/HkFbuBDKjrfyx788i96akwsQ2ErT+dY7Op
# U9OqComYmmn1FrCpugyqm5y8/Cwb7Mz9ozHEKZiB1BjuezA6qb8aRfjgzs0/jGGa
# G07NL1J19Lji7BKjGCxtsMslYKBo3lj64fvKtjHVvEjNAlMYbuZwGCpArYYwBFmE
# vfMEsUARocAAnNEjCznbQUjBWzXiP6IQ0+8iXAMRckITS4Ef
# SIG # End signature block