From f96c65f795ae8265be71b645ae62d5fb10bad809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kecskem=C3=A9ti=20L=C3=A1szl=C3=B3?= Date: Wed, 27 Mar 2024 01:53:17 +0100 Subject: [PATCH] Working kinda --- .gitignore | 2 + Listener.cs | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Program.cs | 22 +++++- config.json | 7 ++ 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 Listener.cs create mode 100644 config.json diff --git a/.gitignore b/.gitignore index 104b544..dc02563 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from `dotnet new gitignore` + +log* # dotenv files .env diff --git a/Listener.cs b/Listener.cs new file mode 100644 index 0000000..e4e092c --- /dev/null +++ b/Listener.cs @@ -0,0 +1,218 @@ +using System.Net; +using System.Net.Sockets; +using Serilog; + +class Listener{ + + public event EventHandler? Stopped; + + private TcpListener listener; + private List clients = new(); + + public Listener(){ + Log.Information("Starting listener"); + this.listener=new TcpListener(IPAddress.Any,Config.listen_port); + listener.Start(); + listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null); + } + + private void AcceptClient(IAsyncResult res){ + TcpClient? client = null; + try{ + client = listener.EndAcceptTcpClient(res); + }catch(Exception ex){ + Log.Error(ex, "Error while accepting incoming connection"); + } + + if(client!=null){ + Client? new_client = null; + try{ + new_client=new Client(client); + }catch(Exception ex){ + Log.Error(ex, "Error creating new client instance"); + } + if(new_client==null){ + client.Close(); + }else{ + clients.Add(new_client); + new_client.Stopped+=ClientStopped; + Log.Information("Client connected: {Endpoint}", client.Client.RemoteEndPoint); + } + } + + try{ + listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null); + }catch(Exception ex){ + Log.Fatal(ex, "Failed to start new client accept cycle"); + Stop(); + } + } + + private void ClientStopped(object? sender, EventArgs e){ + clients.Remove((Client)sender!); + } + + public void Stop(){ + Log.Information("Stopping listener"); + listener.Stop(); + while(clients.Count>0){ + clients[0].Stop(); + } + Stopped?.Invoke(this,EventArgs.Empty); + } + + + private class Client{ + public event EventHandler? Stopped; + + private TcpClient client; + private NetworkStream stream; + private CancellationTokenSource cts = new CancellationTokenSource(); + + private EndPoint endPoint; + + private TcpClient? relay_client; + private NetworkStream? relay_stream; + + long data_recv = 0; + long data_sent = 0; + + public Client(TcpClient client){ + this.client=client; + endPoint=client.Client.RemoteEndPoint!; + Log.Debug("Starting client handler for {Endpoint}", endPoint); + stream=client.GetStream(); + client.NoDelay=true; + + _ = Identify(); + } + + private Task Identify(){ + return Task.Run(async ()=>{ + Log.Debug("Trying to identify {Endpoint}", endPoint); + byte[] packet = new byte[16]; + int packet_size; + try{ + packet_size = await stream.ReadAtLeastAsync(packet, 1, false, cts.Token); + }catch(Exception ex){ + Log.Information(ex, "Error while recieving from {Endpoint}", endPoint); + Stop(); + return; + } + + if(packet_size==0){ + Log.Information("Client disconnected {Endpoint}", endPoint); + Stop(); + return; + } + + bool tls; + if(packet[0] != 0x16 /*tls magic byte*/){ + Log.Information("Client {Endpoint} identified as TCP", endPoint); + // simple tcp connection + tls=false; + }else{ + Log.Information("Client {Endpoint} identified as TLS", endPoint); + // tls connection + tls=true; + } + try{ + relay_client = new TcpClient(tls ? Config.ssl_address : Config.tcp_address, tls ? Config.ssl_port : Config.tcp_port); + relay_stream = relay_client.GetStream(); + relay_client.NoDelay=true; + relay_stream.Write(packet, 0, packet_size); + data_recv+=packet_size; + + Log.Information("Starting relay for {Endpoint}", endPoint); + + _ = RelayFromClient(); + _ = RelayToClient(); + }catch(Exception ex){ + Log.Error(ex, "Failed to connect client to relay server {Endpoint}", endPoint); + Stop(); + return; + } + }); + } + + private Task RelayFromClient(){ + return Task.Run(async ()=>{ + try{ + byte[] buffer = new byte[4096]; + int bytes_read = 0; + while(!cts.Token.IsCancellationRequested){ + bytes_read = await stream.ReadAsync(buffer, cts.Token); + data_recv+=bytes_read; + if(bytes_read == 0){ + Log.Information("Client disconnected {Endpoint}", endPoint); + Stop(); + break; + } + await relay_stream!.WriteAsync(buffer.AsMemory(0, bytes_read), cts.Token); + } + }catch(TaskCanceledException){ + }catch(OperationCanceledException){ + }catch(AggregateException ex){ + if(ex.InnerException is not TaskCanceledException){ + Log.Debug(ex, "Error while recieving from client {Endpoint}", endPoint); + Stop(); + } + }catch(Exception ex){ + Log.Debug(ex, "Error while recieving from client {Endpoint}", endPoint); + Stop(); + } + }); + } + + private Task RelayToClient(){ + return Task.Run(async ()=>{ + try{ + byte[] buffer = new byte[4096]; + int bytes_read = 0; + while(!cts.Token.IsCancellationRequested){ + bytes_read = await relay_stream!.ReadAsync(buffer, cts.Token); + data_sent+=bytes_read; + if(bytes_read == 0){ + Log.Information("Relay disconnected {Endpoint}", endPoint); + Stop(); + break; + } + await stream!.WriteAsync(buffer.AsMemory(0, bytes_read), cts.Token); + } + }catch(TaskCanceledException){ + }catch(OperationCanceledException){ + }catch(AggregateException ex){ + if(ex.InnerException is not TaskCanceledException){ + Log.Debug(ex, "Error while recieving from relay {Endpoint}", endPoint); + Stop(); + } + }catch(Exception ex){ + Log.Debug(ex, "Error while recieving from relay {Endpoint}", endPoint); + Stop(); + } + }); + } + + + public void Stop(){ + if(cts.Token.IsCancellationRequested) return; + Log.Information("Stopping client for {Endpoint}\nTotal: Tx: {Transmit} bytes, Rx: {Recieve} bytes", endPoint, data_sent, data_recv); + cts.Cancel(); + try{ + client.Close(); + }catch(Exception ex){ + Log.Error(ex, "Error closing client: {Endpoint}", endPoint); + } + if(relay_client != null){ + try{ + relay_client.Close(); + }catch(Exception ex){ + Log.Error(ex, "Error closing relay: {Endpoint}", endPoint); + } + } + + Stopped?.Invoke(this, EventArgs.Empty); + } + } + +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index aa6b2df..b3bcbbd 100644 --- a/Program.cs +++ b/Program.cs @@ -7,10 +7,28 @@ internal class Program Log.Logger = new LoggerConfiguration() .WriteTo.Console() - .WriteTo.File($"log-{DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")}.txt") + .WriteTo.File($"log-{DateTime.Now:yyyy-MM-dd-hh-mm-ss}.txt") .MinimumLevel.Debug() .CreateLogger(); - + Listener listener = new Listener(); + + AutoResetEvent quit = new AutoResetEvent(false); + + Console.CancelKeyPress+=(object? s, ConsoleCancelEventArgs e)=>{ + e.Cancel=true; + quit.Set(); + }; + + listener.Stopped+=(object? s, EventArgs e)=>{ + quit.Set(); + }; + + quit.WaitOne(); + Log.Information("Stopping..."); + + listener.Stop(); + + Log.Information("Exit"); } } \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..482b0ac --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "listen_port":"8080", + "ssl_address":"localhost", + "ssl_port":3000, + "tcp_address":"localhost", + "tcp_port":25569 +} \ No newline at end of file