Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
jjxtra committed Apr 21, 2024
1 parent 3828ef7 commit 4a8962f
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 1 deletion.
33 changes: 33 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Net;

// validate usage
if (args.Length != 2)
{
Console.WriteLine("Usage: <localip:localport> <remoteip:remoteport>");
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;
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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 <localip:localport> <remoteip:remoteport>`

## 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
218 changes: 218 additions & 0 deletions SocketCloser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace SocketCloser;

/// <summary>
/// Socket closer interface
/// </summary>
public interface ISocketCloser
{
/// <summary>
/// Close a socket using low level windows API. Handles ipv4 and ipv6.
/// </summary>
/// <param name="local">Local end point</param>
/// <param name="remote">Remote end point</param>
/// <returns>True if closed, false if not</returns>
bool CloseSocket(IPEndPoint local, IPEndPoint remote);
}

/// <summary>
/// Close sockets on Windows or Linux
/// </summary>
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<KillTcpSocketData_V6>();

[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);
}

/// <inheritdoc />
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<byte> 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;
}
*/
11 changes: 11 additions & 0 deletions SocketCloser.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>
25 changes: 25 additions & 0 deletions SocketCloser.sln
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 4a8962f

Please sign in to comment.