Dotnet socket, toxiproxy like, weekend experimentations

I stumbled upon this tweet which revive an inner side project idea that I had.

I've always wanted to code something like toxiproxy. Mess around with my own implementation of a proxy. Let's start simple with a simple tcp socket server that echo exactly the same message to another server.


First of all, this code is written with the new minimal api of .NET 6. Main? Program? Gone, well not gone, just somewhere that we just don't really care. We can still have access to our precious args.

No console project to start with?

mkdir <app_name>
cd <app_name>
dotnet new sln --name <solution_name>
dotnet new console --name <project_name> --output <project_name>
dotnet sln add .\<project_name>\<project_name>.csproj

Let's initialize our tcp stream on port 8081. Keep in mind that you will need a way to send data to this server. Notice the using statement, the lack of curly, our socket will get disposed at the end of the scope, in this case, at the end of the program.

using var listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 8081));
 
Console.WriteLine($"Listening on {listenSocket.LocalEndPoint}");
 
listenSocket.Listen();

Socket is ready, listening on port 8081.

Another socket connection is needed to be able to send the data.

using var connectSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
connectSocket.Connect(new IPEndPoint(IPAddress.Loopback, 8080));

That's it. Connection is established with the other server. You can use any tcp server for this. We just need a willing target.

The next part will handle the bytes exchange. We need to receive some bytes and send them to the connected server.

while (true)
{
    var connection = await listenSocket.AcceptAsync();
 
    _ = Task.Run(async () =>
    {
        var buffer = new byte[4096];
        try
        {
            while (true)
            {
                int read = await connection.ReceiveAsync(buffer, SocketFlags.None);
                if (read == 0)
                {
                    break;
                }
 
                var readBuffer = buffer[..read];
 
                await connectSocket.SendAsync(readBuffer, SocketFlags.None);
            }
        }
        finally
        {
            connection.Dispose();
        }
    });
}

After connection acceptance, it's important to initialize a buffer ln 7, bytes array. Length of this buffer matters since bytes read will be written in this one ln 12. ReceiveAsync returned the amount of bytes read. 0 bytes read means that the connection is over ln 13.

buffer[..read] this line holds a decent amount of information. .. is the range operator in C#. The line can be read as take all values between index 0 to read (which is the amount of bytes read).

Then send the resulting buffer readBuffer to the target server with connectSocket.SendAsync

That's it, pretty nice. To add some toxicity in the exchange, we could add a delay before sending the data to simulate throttling.