Adam Pippin
3 years ago
18 changed files with 575 additions and 0 deletions
@ -0,0 +1,17 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<OutputType>Exe</OutputType> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.1.4" /> |
||||
|
<PackageReference Include="System.Management.Automation" Version="7.1.4" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\KnockKnock.Lib\KnockKnock.Lib.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,40 @@ |
|||||
|
using System; |
||||
|
using System.Net; |
||||
|
using System.Management.Automation; |
||||
|
|
||||
|
namespace KnockKnock.CLI |
||||
|
{ |
||||
|
internal class Program |
||||
|
{ |
||||
|
static void Main(string[] args) |
||||
|
{ |
||||
|
Lib.Manager m = new Lib.Manager(); |
||||
|
Lib.Proxy p = new Lib.Proxy(new IPEndPoint(IPAddress.Any, 1234), new IPEndPoint(IPAddress.Parse("10.200.0.10"), 3389)); |
||||
|
p.Idle += P_Idle; |
||||
|
p.Active += P_Active; |
||||
|
m.AddProxy(p); |
||||
|
//m.AddProxy(new IPEndPoint(IPAddress.Any, 1234), new IPEndPoint(IPAddress.Parse("172.217.14.195"), 80));
|
||||
|
P_Active(p); |
||||
|
Console.WriteLine("Running"); |
||||
|
Console.ReadLine(); |
||||
|
m.Close(); |
||||
|
Console.WriteLine("Good night!"); |
||||
|
} |
||||
|
|
||||
|
private static void P_Active(Lib.Proxy sender) |
||||
|
{ |
||||
|
Console.WriteLine("Active!"); |
||||
|
PowerShell ps = PowerShell.Create(); |
||||
|
ps.AddScript("Resume-VM Debian"); |
||||
|
foreach (PSObject result in ps.Invoke()) |
||||
|
{ |
||||
|
Console.WriteLine(result); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void P_Idle(Lib.Proxy sender) |
||||
|
{ |
||||
|
Console.WriteLine("Idle"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
internal class Connection |
||||
|
{ |
||||
|
public delegate void SocketReceivedEvent(Connection sender, byte[] data); |
||||
|
public delegate void SocketEvent(Connection sender); |
||||
|
|
||||
|
public SocketReceivedEvent Received; |
||||
|
public SocketEvent Closed; |
||||
|
|
||||
|
const int BUFFER_SIZE = 1024; |
||||
|
|
||||
|
Socket socket; |
||||
|
byte[] buffer = new byte[BUFFER_SIZE]; |
||||
|
|
||||
|
public Connection(Socket socket) |
||||
|
{ |
||||
|
this.socket = socket; |
||||
|
} |
||||
|
|
||||
|
public void Open() |
||||
|
{ |
||||
|
this.socket.BeginReceive(this.buffer, 0, BUFFER_SIZE, SocketFlags.None, this.SocketReceive, null); |
||||
|
} |
||||
|
|
||||
|
public void Close() |
||||
|
{ |
||||
|
this.socket.Close(); |
||||
|
} |
||||
|
|
||||
|
public void Send(byte[] data) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
this.socket.Send(data); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
this.Closed(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void SocketReceive(IAsyncResult result) |
||||
|
{ |
||||
|
int read; |
||||
|
try |
||||
|
{ |
||||
|
read = socket.EndReceive(result); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
Closed(this); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (read == 0) |
||||
|
{ |
||||
|
// Connection closed
|
||||
|
Closed(this); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
byte[] received = new byte[read]; |
||||
|
Array.Copy(this.buffer, 0, received, 0, read); |
||||
|
Received(this, received); |
||||
|
try |
||||
|
{ |
||||
|
socket.BeginReceive(this.buffer, 0, BUFFER_SIZE, SocketFlags.None, this.SocketReceive, null); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
Closed(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,78 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
internal class ConnectionPool |
||||
|
{ |
||||
|
public delegate void PoolEvent(ConnectionPool sender); |
||||
|
|
||||
|
public event PoolEvent Closed; |
||||
|
|
||||
|
List<Connection> connections; |
||||
|
|
||||
|
public ConnectionPool() |
||||
|
{ |
||||
|
connections = new List<Connection>(); |
||||
|
} |
||||
|
|
||||
|
public void Open() |
||||
|
{ |
||||
|
foreach (Connection c in this.connections) |
||||
|
{ |
||||
|
c.Open(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void AddConnection(Connection c) |
||||
|
{ |
||||
|
this.connections.Add(c); |
||||
|
c.Received += Connection_Receive; |
||||
|
c.Closed += Connection_Closed; |
||||
|
} |
||||
|
|
||||
|
public void RemoveConnection(Connection c) |
||||
|
{ |
||||
|
this.connections.Remove(c); |
||||
|
c.Received -= Connection_Receive; |
||||
|
c.Closed -= Connection_Closed; |
||||
|
} |
||||
|
|
||||
|
public void Close() |
||||
|
{ |
||||
|
foreach (Connection c in this.connections) |
||||
|
{ |
||||
|
c.Close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void Connection_Receive(Connection sender, byte[] data) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine("Receive " + data.Length + " bytes"); |
||||
|
foreach (Connection c in this.connections) |
||||
|
{ |
||||
|
if (c != sender) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine("Send " + data.Length + " bytes"); |
||||
|
c.Send(data); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void Connection_Closed(Connection sender) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine("Closed"); |
||||
|
foreach (Connection c in this.connections) |
||||
|
{ |
||||
|
if (c != sender) |
||||
|
{ |
||||
|
c.Close(); |
||||
|
} |
||||
|
} |
||||
|
Closed(this); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net5.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,47 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
internal class Listener |
||||
|
{ |
||||
|
public delegate void ConnectedEvent(Listener sender, Socket socket); |
||||
|
public event ConnectedEvent Connected; |
||||
|
|
||||
|
int port; |
||||
|
Socket listener; |
||||
|
|
||||
|
|
||||
|
public Listener(int port) |
||||
|
{ |
||||
|
this.port = port; |
||||
|
} |
||||
|
|
||||
|
public void Start() |
||||
|
{ |
||||
|
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
listener.Bind(new IPEndPoint(IPAddress.Loopback, this.port)); |
||||
|
listener.Listen(); |
||||
|
listener.BeginAccept(this.ListenerAcceptCallback, null); |
||||
|
} |
||||
|
|
||||
|
public void Stop() |
||||
|
{ |
||||
|
listener.Close(); |
||||
|
listener = null; |
||||
|
} |
||||
|
|
||||
|
protected void ListenerAcceptCallback(IAsyncResult result) |
||||
|
{ |
||||
|
if (listener == null) return; |
||||
|
Socket client = listener.EndAccept(result); |
||||
|
listener.BeginAccept(this.ListenerAcceptCallback, null); |
||||
|
this.Connected(this, client); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
public class Manager |
||||
|
{ |
||||
|
List<Proxy> proxies; |
||||
|
|
||||
|
public Manager() |
||||
|
{ |
||||
|
proxies = new List<Proxy>(); |
||||
|
} |
||||
|
|
||||
|
public void AddProxy(Proxy p) |
||||
|
{ |
||||
|
proxies.Add(p); |
||||
|
p.Start(); |
||||
|
} |
||||
|
|
||||
|
public void Close() |
||||
|
{ |
||||
|
foreach (Proxy p in this.proxies) |
||||
|
{ |
||||
|
p.Stop(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
public class Proxy |
||||
|
{ |
||||
|
public delegate void ProxyEvent(Proxy sender); |
||||
|
|
||||
|
public event ProxyEvent Active; |
||||
|
public event ProxyEvent Idle; |
||||
|
|
||||
|
Listener listener; |
||||
|
Upstream upstream; |
||||
|
List<ConnectionPool> connections; |
||||
|
|
||||
|
|
||||
|
public Proxy(IPEndPoint listener, IPEndPoint upstream) |
||||
|
{ |
||||
|
this.listener = new Listener(listener.Port); |
||||
|
this.upstream = new Upstream(upstream.Address, upstream.Port); |
||||
|
this.connections = new List<ConnectionPool>(); |
||||
|
} |
||||
|
|
||||
|
public void Start() |
||||
|
{ |
||||
|
this.listener.Connected += Listener_Connected; |
||||
|
this.listener.Start(); |
||||
|
} |
||||
|
|
||||
|
public void Stop() |
||||
|
{ |
||||
|
foreach (ConnectionPool p in connections) |
||||
|
{ |
||||
|
p.Close(); |
||||
|
} |
||||
|
connections.Clear(); |
||||
|
} |
||||
|
|
||||
|
private void Listener_Connected(Listener sender, Socket client_socket) |
||||
|
{ |
||||
|
if (this.connections.Count == 0 && this.Active != null) |
||||
|
{ |
||||
|
this.Active(this); |
||||
|
} |
||||
|
Socket upstream_socket = this.upstream.Connect(); |
||||
|
|
||||
|
Connection client_conn = new Connection(client_socket); |
||||
|
Connection upstream_conn = new Connection(upstream_socket); |
||||
|
|
||||
|
ConnectionPool cp = new ConnectionPool(); |
||||
|
cp.AddConnection(client_conn); |
||||
|
cp.AddConnection(upstream_conn); |
||||
|
cp.Closed += Connection_Closed; |
||||
|
cp.Open(); |
||||
|
} |
||||
|
|
||||
|
private void Connection_Closed(ConnectionPool sender) |
||||
|
{ |
||||
|
connections.Remove(sender); |
||||
|
if (connections.Count == 0 && this.Idle != null) |
||||
|
{ |
||||
|
this.Idle(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
|
||||
|
namespace KnockKnock.Lib |
||||
|
{ |
||||
|
internal class Upstream |
||||
|
{ |
||||
|
IPAddress ip; |
||||
|
int port; |
||||
|
|
||||
|
|
||||
|
public Upstream(IPAddress ip, int port) |
||||
|
{ |
||||
|
this.ip = ip; |
||||
|
this.port = port; |
||||
|
} |
||||
|
|
||||
|
public Socket Connect() |
||||
|
{ |
||||
|
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
s.Connect(this.ip, this.port); |
||||
|
return s; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.Worker"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<UserSecretsId>dotnet-KnockKnock.Service-7A5DE5F7-7176-47CB-A545-C289BBFCF99D</UserSecretsId> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0-rc.1.21451.13" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" /> |
||||
|
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.1.4" /> |
||||
|
<PackageReference Include="System.Management.Automation" Version="7.1.4" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\KnockKnock.Lib\KnockKnock.Lib.csproj" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
@ -0,0 +1,11 @@ |
|||||
|
using KnockKnock.Service; |
||||
|
|
||||
|
IHost host = Host.CreateDefaultBuilder(args) |
||||
|
.ConfigureServices(services => |
||||
|
{ |
||||
|
services.AddHostedService<Worker>(); |
||||
|
}) |
||||
|
.UseWindowsService() |
||||
|
.Build(); |
||||
|
|
||||
|
await host.RunAsync(); |
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"profiles": { |
||||
|
"KnockKnock.Service": { |
||||
|
"commandName": "Project", |
||||
|
"dotnetRunMessages": true, |
||||
|
"environmentVariables": { |
||||
|
"DOTNET_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
namespace KnockKnock.Service; |
||||
|
using System.Net; |
||||
|
using System.Management.Automation; |
||||
|
using System.Timers; |
||||
|
|
||||
|
public class Worker : BackgroundService |
||||
|
{ |
||||
|
private readonly ILogger<Worker> _logger; |
||||
|
|
||||
|
public Worker(ILogger<Worker> logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
Lib.Manager manager; |
||||
|
Timer idleTimer; |
||||
|
|
||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
||||
|
{ |
||||
|
_logger.LogInformation("Starting KnockKnock"); |
||||
|
|
||||
|
manager = new Lib.Manager(); |
||||
|
|
||||
|
_logger.LogInformation("Creating proxy for :1234 -> 10.200.0.10:3389"); |
||||
|
Lib.Proxy p = new Lib.Proxy(new IPEndPoint(IPAddress.Any, 1234), new IPEndPoint(IPAddress.Parse("10.200.0.10"), 3389)); |
||||
|
p.Active += Proxy_Active; |
||||
|
p.Idle += Proxy_Idle; |
||||
|
|
||||
|
_logger.LogInformation("Instantiating Idle Timer"); |
||||
|
idleTimer = new Timer(10000); |
||||
|
idleTimer.Elapsed += IdleTimer_Elapsed; |
||||
|
|
||||
|
_logger.LogInformation("Starting"); |
||||
|
manager.AddProxy(p); |
||||
|
|
||||
|
while (!stoppingToken.IsCancellationRequested) |
||||
|
{ |
||||
|
await Task.Delay(1000, stoppingToken); |
||||
|
} |
||||
|
|
||||
|
manager.Close(); |
||||
|
} |
||||
|
|
||||
|
private void IdleTimer_Elapsed(object? sender, ElapsedEventArgs e) |
||||
|
{ |
||||
|
idleTimer.Stop(); |
||||
|
_logger.LogDebug("Suspending VM"); |
||||
|
PowerShell ps = PowerShell.Create(); |
||||
|
ps.AddScript("Suspend-VM Debian"); |
||||
|
ps.Invoke(); |
||||
|
} |
||||
|
|
||||
|
private void Proxy_Idle(Lib.Proxy sender) |
||||
|
{ |
||||
|
_logger.LogDebug("Idle -- starting idle timer"); |
||||
|
idleTimer.Start(); |
||||
|
} |
||||
|
|
||||
|
private void Proxy_Active(Lib.Proxy sender) |
||||
|
{ |
||||
|
idleTimer.Stop(); |
||||
|
_logger.LogDebug("Resuming VM"); |
||||
|
PowerShell ps = PowerShell.Create(); |
||||
|
ps.AddScript("Resume-VM Debian"); |
||||
|
ps.Invoke(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.Hosting.Lifetime": "Information" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.Hosting.Lifetime": "Information" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
sc create "KnockKnock" start=auto binpath="%~d0%~p0\bin\Release\net6.0\KnockKnock.Service.exe" |
@ -0,0 +1,2 @@ |
|||||
|
sc stop "KnockKnock" |
||||
|
sc delete "KnockKnock" |
@ -0,0 +1,37 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio Version 17 |
||||
|
VisualStudioVersion = 17.0.31717.71 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KnockKnock.Lib", "KnockKnock.Lib\KnockKnock.Lib.csproj", "{AE473668-717A-422D-A319-E1F88859EF6B}" |
||||
|
EndProject |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KnockKnock.CLI", "KnockKnock.CLI\KnockKnock.CLI.csproj", "{1AB960B3-F48A-4787-A031-3D938595A2E0}" |
||||
|
EndProject |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KnockKnock.Service", "KnockKnock.Service\KnockKnock.Service.csproj", "{B82D18F9-BEF8-461F-898B-EE81F486C20D}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{AE473668-717A-422D-A319-E1F88859EF6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{AE473668-717A-422D-A319-E1F88859EF6B}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{AE473668-717A-422D-A319-E1F88859EF6B}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{AE473668-717A-422D-A319-E1F88859EF6B}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{1AB960B3-F48A-4787-A031-3D938595A2E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{1AB960B3-F48A-4787-A031-3D938595A2E0}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{1AB960B3-F48A-4787-A031-3D938595A2E0}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{1AB960B3-F48A-4787-A031-3D938595A2E0}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{B82D18F9-BEF8-461F-898B-EE81F486C20D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{B82D18F9-BEF8-461F-898B-EE81F486C20D}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{B82D18F9-BEF8-461F-898B-EE81F486C20D}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{B82D18F9-BEF8-461F-898B-EE81F486C20D}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ExtensibilityGlobals) = postSolution |
||||
|
SolutionGuid = {98F24DFD-A873-4227-B0EF-6CCD67208132} |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
Loading…
Reference in new issue