6.18.2011

Listing All Tcp connections on Local Computer

For this part of code, it will be based on this Api called GetExtendedTcpTable.
What we are going to do is, to try and retrieve every tcp connection, its state, its local and remote adresses and ports, and it's owning process. We could also use GetTcpTable to for the same job, but i noticed that it is only available in Vista, so we'll go with GetExtendedTcpTable for now, as it is working on both Vista and Xp Sp2.

Let's start with coding, here is a detailed module about what we need, so create a new module and paste this :

Imports System.Runtime.InteropServices
Imports System.Net.NetworkInformation
Imports System.Net

Public Module TCPGet
'Api to get table
Declare Auto Function GetExtendedTcpTable Lib "iphlpapi.dll" (ByVal pTCPTable As IntPtr, ByRef OutLen As Integer, ByVal Sort As Boolean, ByVal IpVersion As Integer, ByVal dwClass As Integer, ByVal Reserved As Integer) As Integer
'const : we are asking for connections with ProcessIDs', all of them.
Const TCP_TABLE_OWNER_PID_ALL As Integer = 5
'represents a full table
<StructLayout(LayoutKind.Sequential)> _
Public Structure MIB_TCPTABLE_OWNER_PID
Public NumberOfEntries As Integer 'number of rows
Public Table As IntPtr 'array of tables
End Structure
'represents a row
<StructLayout(LayoutKind.Sequential)> _
Public Structure MIB_TCPROW_OWNER_PID
Public state As Integer 'state of the connection
Public localAddress As UInteger
Public LocalPort As Integer
Public RemoteAddress As UInteger
Public remotePort As Integer
Public PID As Integer 'Process ID
End Structure
'this a structure that we made to store a connection after formatting it
Structure TcpConnection
Public State As TcpState
Public localAddress As String
Public LocalPort As Integer
Public RemoteAddress As String
Public remotePort As Integer
Public Proc As String
End Structure
'Main Function
Function GetAllTCPConnections() As MIB_TCPROW_OWNER_PID()
GetAllTCPConnections = Nothing
'we need to know how much memory we need to store all the connections
Dim cb As Integer
'so we call GetExtendedTcpTable without a pointer to a table to get the size
GetExtendedTcpTable(Nothing, cb, False, 2, TCP_TABLE_OWNER_PID_ALL, 0)
'after getting the size, we allocate memory
Dim tcptable As IntPtr = Marshal.AllocHGlobal(cb)
'the call where we store the connections in that memory
If GetExtendedTcpTable(tcptable, cb, False, 2, TCP_TABLE_OWNER_PID_ALL, 0) = 0 Then
'we convert it to a MIB_TCPTABLE_OWNER_PID variable
Dim tab As MIB_TCPTABLE_OWNER_PID = Marshal.PtrToStructure(tcptable, GetType(MIB_TCPTABLE_OWNER_PID))
'and we make an array with a size of number of connections
Dim Mibs(tab.NumberOfEntries - 1) As MIB_TCPROW_OWNER_PID
'row represents the adress of a MIB_TCPROW in memory
Dim row As IntPtr
'for each connection
For i As Integer = 0 To tab.NumberOfEntries - 1
'adress = adress of table + 4(int32) + (size of a MIB_TCPROW * i)
row = New IntPtr(tcptable.ToInt32 + Marshal.SizeOf(tab.NumberOfEntries) + Marshal.SizeOf(GetType(MIB_TCPROW_OWNER_PID)) * i)
'convert pointer to a MIB_TCPROW, and store it in the array
Mibs(i) = Marshal.PtrToStructure(row, GetType(MIB_TCPROW_OWNER_PID))
Next
'returning the array
GetAllTCPConnections = Mibs
End If
'just before leaving, clean up your memory!
Marshal.FreeHGlobal(tcptable)
End Function
'MIB_TCPROW_OWNER_PID is not well formatted, we need to format it.
Function MIB_ROW_To_TCP(ByVal row As MIB_TCPROW_OWNER_PID) As TcpConnection
Dim tcp As New TcpConnection
tcp.State = DirectCast(row.state, TcpState) 'a State enum is better than an int
'IP adresses are stored in long format, we can simply convert it this way
Dim ipad As New IPAddress(row.localAddress)
tcp.localAddress = ipad.ToString
'also the port is in network byte order,we need to convert it as well
tcp.LocalPort = row.LocalPort / 256 + (row.LocalPort Mod 256) * 256
'another ip
ipad = New IPAddress(row.RemoteAddress)
tcp.RemoteAddress = ipad.ToString
'another port
tcp.remotePort = row.remotePort / 256 + (row.remotePort Mod 256) * 256
'for the process, all we get is the ID. Let's get also the name.
Dim p As Process = Process.GetProcessById(row.PID)
tcp.Proc = p.ProcessName & " (" & row.PID.ToString & ")"
'just freeing this variable since we don't need it anymore.
p.Dispose()
'done.
Return tcp
End Function
End Module

Let's Create a Listview as the following to display connections :


Now let's call our function and add each connection to this listview :

'Clear Items
        ListView1.Items.Clear()
        'For Each MIB_TCPROW
        For Each Row In GetAllTCPConnections()
            'We Convert It
            Dim Tcp As TcpConnection = MIB_ROW_To_TCP(Row)
            'Add SubItems to a ListViewItem
            Dim l As New ListViewItem(New String() {Tcp.State.ToString, Tcp.localAddress, Tcp.LocalPort, Tcp.RemoteAddress, Tcp.remotePort, Tcp.Proc})
            'Add The Item
            ListView1.Items.Add(l)
        Next

That's about it, we can now see all tcp connections thanks to this great API.
As for UDP Connections, we can use the GetExtendedUdpTable Function with MIB_UDPTABLE_OWNER_PID and MIB_UDPROW_OWNER_PID Structures with a very similar piece of code to list them in an array, and show them in a listview if you like so.

2 comments:

Anonymous said...

Hi, this is very helpful.
Thank you very much.

Anonymous said...

Hi, any chance to example of GetExtendedudpTable?
Thanks