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