Introduction
The main purpose of this library is to watch the UDP/TCP connections that are active ( like netstat does ) on your PC; it is mainly a wrapper to the IpHelperApi.dll, and it implements 4 generic functions that get statistics about TCP and UDP connections (GetUdpStats()/
GetTcpStats()
) and also get the tables for UDP and TCP connections (GetUdpConnexions()
/ GetTcpConnexions()
).
It also implements 2 undocumented functions of the IpHelperApi.dll that are similar to GetUdpConnexions
/GetTcpConnexions
except that they get the processID
attached to the connection; in the Win 32 API they are named : AllocateAndGetTcpExTableFromStack
and AllocateAndGetUdpExTableFromStack
.
Wrapping the IpHlpAPI.dll
The library is named IPHelper
and it is just a wrapper to IpHelperAPI.dll using P/Invoke mechanism of the .NET framework In the IPHlpAPI32.cs file are all the declarations of the functions and structs from the IPHlpApi.dll; it uses standard attributes from the System.Runtime.InteropServices
namespace.
[DllImport("iphlpapi.dll",SetLastError=true)]
public extern static int GetUdpStatistics (ref MIB_UDPSTATS pStats );
The SetLastError=true
flag allow us to get info about any errors raised in the P/Invoke mechanism, usually the error code is returned by the API function but this code is not very self-explanatory so I included a function to get the message description using the FormatMessage
function of the kernell32.dll :
public static string GetAPIErrorMessageDescription(int ApiErrNumber )
{
System.Text.StringBuilder sError = new System.Text.StringBuilder(512);
int lErrorMessageLength;
lErrorMessageLength = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
(IntPtr)0, ApiErrNumber, 0, sError, sError.Capacity, (IntPtr)0) ;
if(lErrorMessageLength > 0) {
string strgError = sError.ToString();
strgError=strgError.Substring(0,strgError.Length-2);
return strgError+" ("+ApiErrNumber.ToString()+")";
}
return "none";
}
This function just gets a message description from the system and if no description is found it returns "none".
In the MSDN docs all the iphlpapi functions fill their own structure using passed in arguments; for simple structures without array, pointer or nested structure, it is very easy to get the structure correctly filled, but for complex structures it could become much harder.
Let's see how to do that :
Getting the results from the API call
Simple structure Wrapping
First let see how to get the result for a simple function : GetTcpStats()
, this function is used to get several info about the TCP connections like, the number of active connections.
In the original library in C++ it is declared like this
typedef struct _MIB_TCPSTATS {
DWORD dwRtoAlgorithm;
DWORD dwNumConns;
}MIB_TCPSTATS, *PMIB_TCPSTATS
DWORD GetTcpStatistics(PMIB_TCPSTATS pStats);
this structure is very simple, all the DWORD
field can be replaced by integer in c# :
[StructLayout(LayoutKind.Sequential)]
public struct MIB_TCPSTATS
{
public int dwRtoAlgorithm;
public int dwNumConns ;
}
[DllImport("iphlpapi.dll",SetLastError=true)]
public extern static int GetTcpStatistics (ref MIB_TCPSTATS pStats );
Note that the ref
keyword is required because in the C++ call, the function wants a pointer to the structure as argument.
So in the IPHelper
class you have a public method that fills the MIB_TCPSTATS
struct :
public void GetTcpStats()
{
TcpStats = new MIB_TCPSTATS();
IPHlpAPI32Wrapper.GetTcpStatistics(ref TcpStats);
}
Pretty straightforward.
Note that the GetUdpStats()
function works exactly in the same way
More complex structure Wrapping
The GetTcpTable()
function gets an array of all the active TCP connections including the local endpoint, the remote endpoint and the connection's state; those results are stored in 2 nested structures
typedef struct _MIB_TCPTABLE {
DWORD dwNumEntries;
MIB_TCPROW table[ANY_SIZE];
} MIB_TCPTABLE, *PMIB_TCPTABLE;
typedef struct _MIB_TCPROW {
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
} MIB_TCPROW, *PMIB_TCPROW
DWORD GetTcpTable(PMIB_TCPTABLE pTcpTable,PDWORD pdwSize,BOOL bOrder);
The GetTcpTable
function takes three params, a pointer to the structure that will hold the results, the size of the buffer pointed to by the pTcpTable
parameter (note that if the buffer is too small, on output the function sets this parameter equal to the required buffer size), and a bool
flag to tell if we want the results to be sorted or not
The main problem here is that we can't use direct marshalling for those 2 structures because there is a undefined size array, but there are several solutions to solve this issue, I choose to use a simple generic method, replace the first param by a byte array.
[DllImport("iphlpapi.dll",SetLastError=true)]
public static extern int GetTcpTable(byte[] pTcpTable,
out int pdwSize, bool bOrder);
Then we have to use this function in the IPHelper
class in the GetTcpConnexion
member method.
The first thing to do is to get the size of the buffer (pTcpTable
) that we need to get all the results, this is done by calling the function with a dumb buffer, the function will then return the size of the necessary buffer in the pdwSize
param; note that is possible because the function has been declared with the out
keyword before the pdwSize
param :
public void GetTcpConnections()
{
int pdwSize = 20000;
byte[] buffer = new byte[pdwSize];
int res = IPHlpAPI32Wrapper.GetTcpTable(buffer, out pdwSize, true);
if (res != NO_ERROR)
{
buffer = new byte[pdwSize];
res = IPHlpAPI32Wrapper.GetTcpTable(buffer, out pdwSize, true);
if (res != 0)
return;
}
}
Once we have the right buffer size we call the function once again, to get the buffer correctly filled, then we just have to parse the byte to get the right value
TcpConnexion = new IpHlpApidotnet.MIB_TCPTABLE();
int nOffset = 0;
TcpConnexion.dwNumEntries = Convert.ToInt32(buffer[nOffset]);
nOffset+=4;
TcpConnexion.table = new MIB_TCPROW[TcpConnexion.dwNumEntries];
for(int i=0; i<TcpConnexion.dwNumEntries;i++)
{
int st = Convert.ToInt32(buffer[nOffset]);
((MIB_TCPROW)(TcpConnexion.table[i])).StrgState=convert_state(st);
((MIB_TCPROW)(TcpConnexion.table[i])).iState = st;
nOffset+=4;
string LocalAdrr = buffer[nOffset].ToString()+"."+
buffer[nOffset+1].ToString()+"."+
buffer[nOffset+2].ToString()+"."+buffer[nOffset+3].ToString();
nOffset+=4;
int LocalPort = (((int)buffer[nOffset])<<8) +
(((int)buffer[nOffset+1])) +
(((int)buffer[nOffset+2])<<24) + (((int)buffer[nOffset+3])<<16);
nOffset+=4;
((MIB_TCPROW)(TcpConnexion.table[i])).Local =
new IPEndPoint(IPAddress.Parse(LocalAdrr),LocalPort);
string RemoteAdrr = buffer[nOffset].ToString()+"."+
buffer[nOffset+1].ToString()+"."+
buffer[nOffset+2].ToString()+"."+buffer[nOffset+3].ToString();
nOffset+=4;
int RemotePort;
if(RemoteAdrr == "0.0.0.0")
{
RemotePort = 0;
}
else
{
RemotePort = (((int)buffer[nOffset])<<8) +
(((int)buffer[nOffset+1])) +
(((int)buffer[nOffset+2])<<24) + (((int)buffer[nOffset+3])<<16);
}
nOffset+=4;
((MIB_TCPROW)(TcpConnexion.table[i])).Remote =
new IPEndPoint(IPAddress.Parse(RemoteAdrr),RemotePort);
}
That's it we have a structure called TcpConnexion
that holds all the active connections.
Note that we had to convert local and remote port form a DWORD
(UInt32
) to an Int16
which is done with bitwise operator. The GetUdpConnexion()
works exactly in the same way.
IpHelperApi Undocumented functions
The GetTcpConnexion()
and GetUdpConnexion()
functions are useful, but they can't be used to control the active connections, that means that we can't close any connections or even get more info of which software is using this connection. To be able to get control over the connection means that we have to know which process is attached to it, but those two functions are only available on windows XP.
After several days of seeking a way to get the processId
attached to a connection I found that the ipHlpApi has some undocumented functions : AllocateAndGetTcpExTableFromStack
and AllocateAndGetUdpExTableFromStack
- those two functions are acting exactly the same way that GetTcpTable
and GetUdpTable
except that they get the processID
attached to the connection, don't ask why they are not documented on MSDN, I really don't know.
So here is how to use them, let's see how AllocateAndGetTcpExTableFromStack
(AllocateAndGetUdpExTableFromStack
works in the same way) is declared in C++
typedef struct _MIB_TCPTABLE_EX
{
DWORD dwNumEntries;
MIB_TCPROW_EX table[ANY_SIZE];
} MIB_TCPTABLE_EX, *PMIB_TCPTABLE_EX;
typedef struct _MIB_TCPROW_EX{
DWORD dwState;
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
DWORD dwProcessId;
} MIB_TCPROW_EX, *PMIB_TCPROW_EX;
AllocateAndGetTcpExTableFromStack(PMIB_TCPTABLE_EX*,
BOOL,HANDLE,DWORD,DWORD);
The first param is a pointer on the structure that holds the result, the 2nd param is a bool
flag to order the result, the 3rd is a pointer to the heap of the process and the two others are flags that I don't know the meaning of.
This function follows the same pattern of GetTcpTable
and the results are stored in a structure that have an undefined size array. I used a solution lightly different to implement this in C# than I used for GetTcpTable
: The safe C# version of pointer.
Don't be afraid it is not so scary :) for those who are not familiar with Pointer let's (briefly) what it is about.
A Pointer is a data structure that store a memory address, for example if you have an integer that holds the number "100", a pointer on this integer will store the address of the value "100" and not the value itself, hope its clear.
So in C#, in a safe context, we use the IntPtr
structure :
[DllImport("iphlpapi.dll",SetLastError=true)]
public extern static int AllocateAndGetTcpExTableFromStack(
ref IntPtr pTable, bool bOrder, IntPtr heap ,int zero,int flags );
[DllImport("kernel32" ,SetLastError= true)]
public static extern IntPtr GetProcessHeap();
To use it I will basically follow the same path that I used for GetTcpTable
: call the function first time to get the number of connections (row) and so to get the correct buffer size, and then call the function a second time to get the correct results.
public void GetExTcpConnections()
{
int rowsize = 24;
int BufferSize = 100000;
IntPtr lpTable = Marshal.AllocHGlobal(BufferSize);
int res = IPHlpAPI32Wrapper.AllocateAndGetTcpExTableFromStack(
ref lpTable, true, IPHlpAPI32Wrapper.GetProcessHeap(),0,2);
if(res!=NO_ERROR)
{
Debug.WriteLine(
"Erreur : "+IPHlpAPI32Wrapper.GetAPIErrorMessageDescription(res)
+" "+res);
return;
}
int CurrentIndex = 0;
int NumEntries= (int)Marshal.ReadIntPtr(lpTable);
lpTable = IntPtr.Zero;
Marshal.FreeHGlobal(lpTable);
}
So we have to pass an IntPtr
to the function, the 1st thing to do is to define an IntPtr
that points to a memory space large enough to store all the result, so, as I said, we have to allocate an arbitrary memory space to call the function a 1st time, then we can get the number of connections with : int NumEntries = (int)Marshal.ReadIntPtr(lpTable);
here we are using a function from the Marshal
class to read the value pointed by a pointer; finally don't forget to free to memory previously allocated to avoid memory leaks.
Now let's get all the data :
BufferSize = (NumEntries*rowsize)+4;
TcpExConnexions = new IpHlpApidotnet.MIB_EXTCPTABLE();
lpTable = Marshal.AllocHGlobal(BufferSize);
res = IPHlpAPI32Wrapper.AllocateAndGetTcpExTableFromStack(
ref lpTable, true,IPHlpAPI32Wrapper.GetProcessHeap() ,0,2);
if(res!=NO_ERROR)
{
Debug.WriteLine("Erreur : "+
IPHlpAPI32Wrapper.GetAPIErrorMessageDescription(res)+
" "+res);
return;
}
IntPtr current = lpTable;
CurrentIndex = 0;
NumEntries = (int)Marshal.ReadIntPtr(current);
TcpExConnexions.dwNumEntries = NumEntries;
TcpExConnexions.table = new MIB_EXTCPROW[NumEntries];
CurrentIndex+=4;
current = (IntPtr)((int)current+CurrentIndex);
for(int i=0; i< NumEntries;i++)
{
TcpExConnexions.table[i].StrgState =
this.convert_state((int)Marshal.ReadIntPtr(current));
TcpExConnexions.table[i].iState = (int)Marshal.ReadIntPtr(current);
current = (IntPtr)((int)current+4);
UInt32 localAddr = (UInt32)Marshal.ReadIntPtr(current);
current = (IntPtr)((int)current+4);
UInt32 localPort = (UInt32)Marshal.ReadIntPtr(current);
current = (IntPtr)((int)current+4);
TcpExConnexions.table[i].Local = new IPEndPoint(localAddr,
(int)convert_Port(localPort));
UInt32 RemoteAddr = (UInt32)Marshal.ReadIntPtr(current);
current = (IntPtr)((int)current+4);
UInt32 RemotePort=0;
if(RemoteAddr!=0)
{
RemotePort = (UInt32)Marshal.ReadIntPtr(current);
RemotePort=convert_Port(RemotePort);
}
current = (IntPtr)((int)current+4);
TcpExConnexions.table[i].Remote = new IPEndPoint(
RemoteAddr,(int)RemotePort);
TcpExConnexions.table[i].dwProcessId =
(int)Marshal.ReadIntPtr(current);
TcpExConnexions.table[i].ProcessName =
this.get_process_name(TcpExConnexions.table[i].dwProcessId);
current = (IntPtr)((int)current+4);
}
Marshal.FreeHGlobal(lpTable);
current = IntPtr.Zero;
So we call again the function with the right buffer size, and then we will "navigate" in the memory with the beginning of the allocated memory as the starting address.
The 1st 4 bytes are the number of entries, then we enter in a loop for each row in the connection table that begins at the starting address + 4 , in the 1st loop we will have the same mechanism : get each value ordered like in the MIB_TCPROW_EX
, and for each value iterate the pointer by 4, do this as many time as the number of rows.
That's it, we have all the connection AND the process ID attached to it, I added a few helper functions that, for example, get the process name by giving the process ID, but they are pretty simple and self-explanatory.
Remember those 2 functions are only available under WinXP.
How to use the library
I wrote a little app to test the lib, it just shows the results of all the functions in the library, in a listview.
Here are the methods of the lib :
GetTcpStats()
fill a MIB_TCPSTATS
structure
GetUdpStats()
fill a MIB_UDPSTATS
structure
GetTcpConnexions()
fill a MIB_TCPTABLE
structure
GetUdpConnexions()
fill a MIB_UDPTABLE
structure
GetExTcpConnexions()
fill a MIB_EXTCPTABLE
structure
GetExUdpConnexions()
fill a MIB_EXUDPTABLE
structure
That's it, hope it will be useful.