From 4a8962f3fda445591c350dada3e5751032e4771e Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 21 Apr 2024 10:32:33 -0600 Subject: [PATCH] Initial version --- Program.cs | 33 +++++++ README.md | 13 ++- SocketCloser.cs | 218 ++++++++++++++++++++++++++++++++++++++++++++ SocketCloser.csproj | 11 +++ SocketCloser.sln | 25 +++++ 5 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 Program.cs create mode 100644 SocketCloser.cs create mode 100644 SocketCloser.csproj create mode 100644 SocketCloser.sln diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..86f9ebd --- /dev/null +++ b/Program.cs @@ -0,0 +1,33 @@ +using System.Net; + +// validate usage +if (args.Length != 2) +{ + Console.WriteLine("Usage: "); + return -1; +} + +// attempt parse local end point +if (!IPEndPoint.TryParse(args[0], out var localEndPoint)) +{ + Console.WriteLine("Invalid local end point: {0}", args[0]); + return -2; +} + +// attempt parse remote end point +if (!IPEndPoint.TryParse(args[1], out var remoteEndPoint)) +{ + Console.WriteLine("Invalid remote end point: {0}", args[1]); + return -3; +} + +// attempt close socket +SocketCloser.SocketCloser closer = new(); +if (!closer.CloseSocket(localEndPoint, remoteEndPoint)) +{ + Console.WriteLine("Failed to close socket for {0} <-> {1}", localEndPoint, remoteEndPoint); + return -4; +} + +// success +return 0; diff --git a/README.md b/README.md index 6ac57fd..a4c7b89 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # SocketCloser - Close ipv4 and ipv6 sockets on Windows and Linux + Close ipv4 and ipv6 sockets on Windows and Linux. + +## Usage +`SocketCloser ` + +## Return value +Exit codes... +0: success +-1: bad argument count +-2: bad local end point +-3: bad remote end point +-4: failed to close socket \ No newline at end of file diff --git a/SocketCloser.cs b/SocketCloser.cs new file mode 100644 index 0000000..3f4d42e --- /dev/null +++ b/SocketCloser.cs @@ -0,0 +1,218 @@ +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace SocketCloser; + +/// +/// Socket closer interface +/// +public interface ISocketCloser +{ + /// + /// Close a socket using low level windows API. Handles ipv4 and ipv6. + /// + /// Local end point + /// Remote end point + /// True if closed, false if not + bool CloseSocket(IPEndPoint local, IPEndPoint remote); +} + +/// +/// Close sockets on Windows or Linux +/// +public partial class SocketCloser : ISocketCloser +{ + private const int MIB_TCP_STATE_DELETE_TCB = 12; + + private static readonly byte[] moduleId = [0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x4A, 0x00, 0xEB, 0x1A, 0x9B, 0xD4, 0x11, 0x91, 0x23, 0x00, 0x50, 0x04, 0x77, 0x59, 0xBC]; + private static readonly IntPtr moduleIdPtr; + private static readonly int killTcpSocketData_V6_Size = Marshal.SizeOf(); + + [LibraryImport("iphlpapi.dll", SetLastError = true)] + private static partial uint SetTcpEntry(ref MIB_TCPROW pTcpRow); + + [LibraryImport("nsi.dll", SetLastError = true)] + private static partial uint NsiSetAllParameters(uint action, uint flags, IntPtr moduleId, uint operation, IntPtr buffer, uint bufferLength, IntPtr metric, uint metricLength); + + [StructLayout(LayoutKind.Sequential)] + private struct MIB_TCPROW + { + public uint dwState; + public uint dwLocalAddr; + public uint dwLocalPort; + public uint dwRemoteAddr; + public uint dwRemotePort; + } + + [StructLayout(LayoutKind.Sequential)] + private struct KillTcpSocketData_V6 + { + public ushort wLocalAddressFamily; + public ushort wLocalPort; + public uint bReserved1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] bLocal; + public uint dwLocalScopeID; + + public ushort wRemoteAddressFamily; + public ushort wRemotePort; + public uint bReserved2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] bRemote; + public uint dwRemoteScopeID; + }; + + static SocketCloser() + { + moduleIdPtr = Marshal.AllocHGlobal(moduleId.Length); + Marshal.Copy(moduleId, 0, moduleIdPtr, moduleId.Length); + } + + /// + public bool CloseSocket(IPEndPoint local, IPEndPoint remote) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return CloseSocketLinux(local, remote); + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return CloseSocketWindows(local, remote); + } + + return false; + } + + private static bool CloseSocketLinux(IPEndPoint local, IPEndPoint remote) + { + // sudo ss --kill state all src IP_ADDRESS:PORT dst IP_ADDRESS:PORT + string command = $"ss --kill state all src \"{local.Address}:{local.Port}\" dst \"{remote.Address}:{remote.Port}\""; + using var proc = Process.Start("sudo", command); + proc.WaitForExit(); + return proc.ExitCode == 0; + } + + private static uint ToUInt32(IPAddress ip) + { + // we can safely assume ip is ipv4 + Span bytes = stackalloc byte[4]; + _ = ip.TryWriteBytes(bytes, out _); + return BitConverter.ToUInt32(bytes); + } + private static bool CloseSocketWindows(IPEndPoint local, IPEndPoint remote) + { + var localPortFixed = (ushort)IPAddress.HostToNetworkOrder((short)local.Port); + var remotePortFixed = (ushort)IPAddress.HostToNetworkOrder((short)remote.Port); + + if (local.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + MIB_TCPROW row = new() + { + dwState = MIB_TCP_STATE_DELETE_TCB, + dwLocalAddr = ToUInt32(local.Address), + dwLocalPort = (uint)localPortFixed, + dwRemoteAddr = ToUInt32(remote.Address), + dwRemotePort = (uint)remotePortFixed + }; + var result = SetTcpEntry(ref row); + return result == 0 || result == 317; + } + else if (local.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + { + KillTcpSocketData_V6 row6 = new() + { + wLocalAddressFamily = (ushort)AddressFamily.InterNetworkV6, + wLocalPort = localPortFixed, + bLocal = local.Address.GetAddressBytes(), + bRemote = remote.Address.GetAddressBytes(), + bReserved1 = 0, + bReserved2 = 0, + dwLocalScopeID = (uint)IPAddress.HostToNetworkOrder(local.Address.ScopeId), + dwRemoteScopeID = (uint)IPAddress.HostToNetworkOrder(remote.Address.ScopeId), + wRemoteAddressFamily = (ushort)AddressFamily.InterNetworkV6, + wRemotePort = remotePortFixed + }; + + var ptr = Marshal.AllocHGlobal(killTcpSocketData_V6_Size); + try + { + Marshal.StructureToPtr(row6, ptr, false); + var result = NsiSetAllParameters(1, 2, moduleIdPtr, 16, ptr, (uint)killTcpSocketData_V6_Size, IntPtr.Zero, 0); + return result == 0 || result == 317; + } + finally + { + // Cleanup + Marshal.FreeHGlobal(ptr); + } + } + + return false; + } +} + +/* +// https://www.x86matthew.com/view_post?id=settcpentry6 +BYTE bGlobal_NPI_MS_TCP_MODULEID[] = { 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x4A, 0x00, 0xEB, 0x1A, 0x9B, 0xD4, 0x11, 0x91, 0x23, 0x00, 0x50, 0x04, 0x77, 0x59, 0xBC }; + +struct KillTcpSocketData_V6 +{ + WORD wLocalAddressFamily; + WORD wLocalPort; + BYTE bReserved1[4]; + BYTE bLocalAddr[16]; + DWORD dwLocalScopeID; + + WORD wRemoteAddressFamily; + WORD wRemotePort; + BYTE bReserved2[4]; + BYTE bRemoteAddr[16]; + DWORD dwRemoteScopeID; +}; + +DWORD KillTcpSocket_V6(MIB_TCP6ROW_OWNER_PID *pTcpRow) +{ + HMODULE hNsiModule = NULL; + DWORD (WINAPI *pNsiSetAllParameters)(DWORD dwStatic, DWORD dwActionCode, LPVOID NPI_MS_MODULEID, DWORD dwIoMainCode, LPVOID lpNetInfoBuffer, DWORD SizeofNetInfoBuffer, LPVOID lpMetricBuffer, DWORD SizeofMetricBuffer) = NULL; + KillTcpSocketData_V6 KillTcpSocketData; + + // load nsi.dll module (vista onwards) + hNsiModule = LoadLibrary("nsi.dll"); + if(hNsiModule != NULL) + { + // find NsiSetAllParameters function + pNsiSetAllParameters = (unsigned long (__stdcall *)(unsigned long,unsigned long,void *,unsigned long,void *,unsigned long,void *,unsigned long))GetProcAddress(hNsiModule, "NsiSetAllParameters"); + if(pNsiSetAllParameters == NULL) + { + return 1; + } + } + + if(pNsiSetAllParameters == NULL) + { + // NsiSetAllParameters not found (win XP or earlier - ipv6 not supported) + return 1; + } + + // set socket data + memset((void*)&KillTcpSocketData, 0, sizeof(KillTcpSocketData)); + KillTcpSocketData.wLocalAddressFamily = AF_INET6; + KillTcpSocketData.wLocalPort = (WORD)pTcpRow->dwLocalPort; + memcpy((void*)KillTcpSocketData.bLocalAddr, (void*)pTcpRow->ucLocalAddr, sizeof(KillTcpSocketData.bLocalAddr)); + KillTcpSocketData.dwLocalScopeID = pTcpRow->dwLocalScopeId; + KillTcpSocketData.wRemoteAddressFamily = AF_INET6; + KillTcpSocketData.wRemotePort = (WORD)pTcpRow->dwRemotePort; + memcpy((void*)KillTcpSocketData.bRemoteAddr, (void*)pTcpRow->ucRemoteAddr, sizeof(KillTcpSocketData.bRemoteAddr)); + KillTcpSocketData.dwRemoteScopeID = pTcpRow->dwRemoteScopeId; + + // kill socket + if(pNsiSetAllParameters(1, 2, (LPVOID)bGlobal_NPI_MS_TCP_MODULEID, 16, (LPVOID)&KillTcpSocketData, sizeof(KillTcpSocketData), 0, 0) != 0) + { + return 1; + } + + return 0; +} +*/ diff --git a/SocketCloser.csproj b/SocketCloser.csproj new file mode 100644 index 0000000..4151372 --- /dev/null +++ b/SocketCloser.csproj @@ -0,0 +1,11 @@ + + + Exe + net8.0 + enable + enable + true + True + true + + diff --git a/SocketCloser.sln b/SocketCloser.sln new file mode 100644 index 0000000..78ffbe3 --- /dev/null +++ b/SocketCloser.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketCloser", "SocketCloser.csproj", "{FE7F49D3-E936-496E-9623-87D6090ABFFF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FE7F49D3-E936-496E-9623-87D6090ABFFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE7F49D3-E936-496E-9623-87D6090ABFFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE7F49D3-E936-496E-9623-87D6090ABFFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE7F49D3-E936-496E-9623-87D6090ABFFF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C7804837-11CD-43D6-8D78-D93A224BD0C9} + EndGlobalSection +EndGlobal