// // TcpConnectionHelper.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; using System.Net.NetworkInformation; using System.Runtime.InteropServices; namespace NosSmooth.Pcap; /// /// A class for obtaining process tcp connections. /// /// /// Works on Windows only so far. /// public static class TcpConnectionHelper { private const int AF_INET = 2; // IP_v4 = System.Net.Sockets.AddressFamily.InterNetwork private const int AF_INET6 = 23; // IP_v6 = System.Net.Sockets.AddressFamily.InterNetworkV6 /// /// Get TCP IPv4 connections of the specified processes. /// /// The process ids to look for. /// Map from process ids to connecitons. /// Thrown if not windows. public static IReadOnlyDictionary> GetConnections(IReadOnlyList processIds) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { throw new NotImplementedException(); } var result = new Dictionary>(); var tcpv4Connections = GetAllTCPv4Connections(); foreach (var connection in tcpv4Connections) { var process = processIds.FirstOrDefault(x => x == connection.OwningPid, -1); if (process != -1) { if (!result.ContainsKey(process)) { result.Add(process, new List()); } result[process].Add ( new TcpConnection ( connection.LocalAddr, (ushort)(connection.LocalPort[1] | (connection.LocalPort[0] << 8)), connection.RemoteAddr, (ushort)(connection.RemotePort[1] | (connection.RemotePort[0] << 8)) ) ); } } return result; } private static List GetAllTCPv4Connections() { return GetTCPConnections(AF_INET); } private static List GetAllTCPv6Connections() { return GetTCPConnections(AF_INET6); } private static List GetTCPConnections(int ipVersion) { // IPR = Row Type, IPT = Table Type TIPR[] tableRows; int buffSize = 0; var dwNumEntriesField = typeof(TIPT).GetField("DwNumEntries"); // how much memory do we need? uint ret = GetExtendedTcpTable ( IntPtr.Zero, ref buffSize, true, ipVersion, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL ); IntPtr tcpTablePtr = Marshal.AllocHGlobal(buffSize); try { ret = GetExtendedTcpTable ( tcpTablePtr, ref buffSize, true, ipVersion, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_ALL ); if (ret != 0) { return new List(); } // get the number of entries in the table TIPT table = (TIPT)Marshal.PtrToStructure(tcpTablePtr, typeof(TIPT))!; int rowStructSize = Marshal.SizeOf(typeof(TIPR)); uint numEntries = (uint)dwNumEntriesField!.GetValue(table)!; // buffer we will be returning tableRows = new TIPR[numEntries]; IntPtr rowPtr = (IntPtr)((long)tcpTablePtr + 4); for (int i = 0; i < numEntries; i++) { TIPR tcpRow = (TIPR)Marshal.PtrToStructure(rowPtr, typeof(TIPR))!; tableRows[i] = tcpRow; rowPtr = (IntPtr)((long)rowPtr + rowStructSize); // next entry } } finally { // Free the Memory Marshal.FreeHGlobal(tcpTablePtr); } return tableRows != null ? tableRows.ToList() : new List(); } // https://msdn2.microsoft.com/en-us/library/aa366913.aspx [StructLayout(LayoutKind.Sequential)] private struct MIB_TCPROW_OWNER_PID { public uint State; public uint LocalAddr; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] LocalPort; public uint RemoteAddr; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] RemotePort; public uint OwningPid; } // https://msdn2.microsoft.com/en-us/library/aa366921.aspx [StructLayout(LayoutKind.Sequential)] private struct MIB_TCPTABLE_OWNER_PID { public uint DwNumEntries; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)] public MIB_TCPROW_OWNER_PID[] Table; } // https://msdn.microsoft.com/en-us/library/aa366896 [StructLayout(LayoutKind.Sequential)] private struct MIB_TCP6ROW_OWNER_PID { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] LocalAddr; public uint LocalScopeId; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] LocalPort; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] RemoteAddr; public uint RemoteScopeId; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public byte[] RemotePort; public uint State; public uint OwningPid; } // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366905 [StructLayout(LayoutKind.Sequential)] private struct MIB_TCP6TABLE_OWNER_PID { public uint DwNumEntries; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)] public MIB_TCP6ROW_OWNER_PID[] Table; } [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedTcpTable ( IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_CLASS tblClass, uint reserved = 0 ); private enum TCP_TABLE_CLASS { TCP_TABLE_BASIC_LISTENER, TCP_TABLE_BASIC_CONNECTIONS, TCP_TABLE_BASIC_ALL, TCP_TABLE_OWNER_PID_LISTENER, TCP_TABLE_OWNER_PID_CONNECTIONS, TCP_TABLE_OWNER_PID_ALL, TCP_TABLE_OWNER_MODULE_LISTENER, TCP_TABLE_OWNER_MODULE_CONNECTIONS, TCP_TABLE_OWNER_MODULE_ALL } }