Show network connections established in session

Version: 1.5.7
Creator Name: Guy Leech
Date Created: 2018-08-21
Date Modified: 2018-11-20
Scripting language: PS
Download Count: 168

Shows established network connections for all processes in a selected user session sorted on the most connections by processing netstat -ano output. Arguments: Show Top - Show the top n connections (default 1000) Resolve IP to Name - Show DNS names rather than IP addresses (default false) Resolve IP via RIPE - Use internet registrar API to resolve non-DNS resolvable names (default false)
Tags: Network connections,netstat

The Script

<#
    ControlUp SBA so summarise netstat TCP data for a given process or all processes

    Cannot use Get-NetTCPConnection as this has to run on 2008R2 which does not have it in PowerShell v3.0

    @guyrleech 2018

    Modification History:

    19/09/18   GRL    Made name and RIPE resolution optional
                      Added port number

    25/09/18   GRL    Column renaming and re-ordering
                      IPv6 addresses processed
#>

## change this depending on whether a session or process context action
[bool]$sessionMode = $true

[int]$top = 10
[int]$processId = -1
[int]$sessionId = -1
[string]$state = 'ESTABLISHED'
[hashtable]$connections = @{}
[hashtable]$otherConnections = @{}
[int]$connectionCount = 0
[int]$outputWidth = 400
[bool]$resolveIPs = $false
[bool]$resolveViaRIPE = $false

## If we are passed 4 arguments then is pid and count otherwise just count as we are a computer action
if( $args.Count -ge 4 )
{
    if( $sessionMode )
    {
        $sessionId = $args[0]
    }
    else
    {
        $processId = $args[0]
    }
    $top = $args[1]
    $resolveIPs = $args[2] -eq "true"
    $resolveViaRIPE = $args[3] -eq "true"
}
elseif( $args.Count -ge 3 )
{
    $top = $args[0]
    $resolveIPs = $args[1] -eq "true"
    $resolveViaRIPE = $args[2] -eq "true"
}
else
{
    Throw "Unexpected number of arguments passed ($($args.Count))"
}

$process = $null
if( $processId -ge 0 )
{
    $process = Get-Process -Id $processId

    if( ! $process )
    {
        Throw "Process with PID $processId does not exist"
    }
}

[hashtable]$processtoSession = @{}

if( $sessionId -ge 0 )
{
    Get-Process | Where-Object { $_.SessionId -eq $sessionId } | ForEach-Object `
    {
        $processtoSession.Add( [int]$_.Id , $true )
    }
}
    
## UDP does not have destinations so filter out
netstat -ano|select -skip 4 | ForEach-Object { $_ -replace '^\s+' , '' -replace '\s+' , ' '} | ConvertFrom-Csv -Deli ' ' -Head @('Protocol','LocalAddress','ForeignAddress','State','PID') `
    | Where-Object { $_.Protocol -notmatch 'UDP' -and ( $_.PID -eq $processId -or ( $sessionId -ge 0 -and $_.PID -and $processtoSession[ [int]$_.PID ] ) -or ( $sessionId -lt 0 -and $processId -lt 0 ) ) } | ForEach-Object `
{
    if( $_.State -match $state )
    {
        $connectionCount++
        ##[string]$address,[string]$port = $_.ForeignAddress -split ':'
        [string]$key = $_.ForeignAddress ## $_.protocol + ' ' + $address
        $connection = $connections[ $key ]
        if( $connection )
        {
            $connection.Count++
            try
            {
                $connection.Pids.Add( $_.PID , $true )
            }
            catch {} ## in case there already
        }
        else
        {
            $connections.Add( $key , [pscustomobject]@{ 'Count' = [int]1 ; Pids = @{ $_.PID = $true } } )
        }
    }
    else
    {
        ## Keep a count of connections in other states
        $otherConnection = $otherConnections[ $_.state ]
        if( $otherConnection )
        {
            $otherConnections.Set_Item( $_.state , [int]$otherConnection + 1 )
        }
        else
        {
            $otherConnections.Add( $_.state , [int]1 )
        }
    }
}

# Altering the size of the PS Buffer
$PSWindow = (Get-Host).UI.RawUI
$WideDimensions = $PSWindow.BufferSize
$WideDimensions.Width = $outputWidth
$PSWindow.BufferSize = $WideDimensions

if( ! $connections -or ! $connections.Count )
{
    [string]$status = "No netstat output matching state `"$state`" found"
    if( $processId -ge 0 )
    {
        $status += " for process id $processId ($($process.Name))"
    }
    elseif( $sessionId -ge 0 )
    {
        $status += " for session id $sessionId"
    }
    Write-Warning $status
}
else
{
    $properties =[System.Collections.ArrayList] @( 'Destination Address','Port','Count' )
    if( $processId -lt 0 )
    {
        $properties.Insert( 0 , 'Process Name' )
    }
    [string]$heading = $null
    if( $top -ge $connectionCount )
    {
        $heading = 'All'
    }
    else
    {
        $heading = "Top $([math]::Min( $top , $connectionCount)) of"
    }
    $heading += " $connectionCount TCP connections matching state `"$state`"" 
    if( $processId -ge 0 )
    {
        $heading += " for process $($process.Name) ($processId)"
    }
    elseif( $sessionId -ge 0 )
    {
        $heading += " for session $sessionId"
    }

    $heading += ' are:'
    Write-Output $heading

    $connections.GetEnumerator()| ForEach-Object `
    {
        ##[string]$protocol,[string]$address = $_.Name -split '\s'
        [string]$address = $null
        [string]$port = $null 
        [bool]$ipv6 = $false
        [string[]]$splitAddress = $_.Name -split ':'

        if( $splitAddress -and $splitAddress.Count -gt 2 )
        {
            [int]$splitAt = $_.Name.ToString().LastIndexOf(':')
            $address = $_.Name.ToString().SubString( 0 , $splitAt ) -replace '^\[' , '' -replace '\]$' , ''
            $port = $_.Name.ToString().SubString( $splitAt + 1 )
            $ipv6 = $true
        }
        else
        {
            $address = $splitAddress[0]
            $port = $splitAddress[1]
        }

        ## if address is still IP address, we'll look it up via DNS or RIPE REST API if requested via parameters

        if( $resolveIPs -or $resolveViaRIPE )
        {
            try
            {
                $resolved = $null
                [ipaddress]$ipAddress = $address ## exception thrown if not valid IP address
                if( $ipAddress -and ( $ipAddress.Address -or $ipAddress.AddressFamily -eq 'InterNetworkV6' ) ) ## filter out 0.0.0.0
                {
                    if( $resolveIPs )
                    {
                        try
                        {
                            $resolved = [System.Net.Dns]::gethostentry( $ipAddress.IPAddressToString )
                        }
                        catch
                        {
                            $resolved = $null
                        }
                        if( $resolved )
                        {
                            $address = $resolved.HostName
                        }
                    }
                    if( ! $resolved -and $resolveViaRIPE )
                    {
                        [string]$registrant = $null
                        Invoke-WebRequest -Uri "https://rest.db.ripe.net/search.json?type=inetnum&type=organisation&query-string=$address&source=ripe&source=apnic-grs&source=arin-grs&source=afrinic-grs&source=lacnic-grs&source=jpirr-grs&source=radb-grs"|select -ExpandProperty Content|ConvertFrom-Json|select -ExpandProperty objects|select -ExpandProperty object|select -ExpandProperty attributes | ForEach-Object `
                        {
                            if( [string]::IsNullOrEmpty( $registrant ) )
                            {
                                ##Write-Warning (($_.attribute|?{$_.Name -ne 'Remarks'}|%{ "{0}={1}" -f $_.Name , $_.Value }) -join ',' )
                                $org = $_.attribute|Where-Object{ $_.name -eq 'org' }|select -ExpandProperty value
                                if( ! [string]::IsNullOrEmpty( $org ) )
                                {
                                    $registrant = $org
                                }
                            }
                        }    
                        if( ! [string]::IsNullOrEmpty( $registrant ) )
                        {
                            $address = "$address (IANA registrant is $registrant)"
                        }
                    }
                }
            }
            catch{ }
        }

        $result = [pscustomobject][ordered]@{
            'Destination Address' = $address
            'Port' = $port
            'Count' = $_.Value.Count
        }
        if( $processId -lt 0 ) ## computer context
        {
            [hashtable]$processes = @{}
            $_.Value.Pids.GetEnumerator()|Select -ExpandProperty Name|ForEach-Object `
            {
                [string]$thisProcess = Get-Process -Id $_ -ErrorAction SilentlyContinue | Select -ExpandProperty Name -ErrorAction SilentlyContinue
                if( [string]::IsNullOrEmpty( $thisProcess ) )
                {
                    $thisProcess = $_ ## can't get process, probably as terminated
                }
                $existingProcess = $processes[ $thisProcess ]
                if( $existingProcess )
                {
                    $processes.Set_Item( $thisProcess , [int]$existingProcess + 1 )
                }
                else
                {
                    $processes.Add( $thisProcess , [int]1 )
                }
            }
            Add-Member -InputObject $result -MemberType NoteProperty -Name 'Process Name' -Value (( $processes.GetEnumerator() | ForEach-Object { if( $_.Value -gt 1 ) { "$($_.Name)($($_.Value))" } else { "$($_.Name)" } } ) -join ',')
        }
        $result
    } | Sort Count -Descending | Select -First $top -Property $properties | Format-Table -AutoSize

    if( $otherConnections -and $otherConnections.Count )
    {
        Write-Output "Connections in other states:"
        $otherConnections.GetEnumerator() | Select @{n='State';e={$_.Name}},@{n='Count';e={$_.Value}} | sort State
    }
    else
    {
        Write-Output "No other connections in other states found"
    }
}