Search code examples
c#socketsssltcplistener

TCP proxy with reading payload


I want to implement a proxy that forwards a TCP (SSL) connection to the correct server, therefore I need to read the first packet. In generell it seems to work, but the problem is, when I read the data with the SslStream, I can not forward it anymore as plain data, as the network stream is alerad read.

using (NetworkStream clientStream = client.GetStream())
using (SslStream sslStream = new SslStream(clientStream, false, new RemoteCertificateValidationCallback(ValidateClientCertificate)))
{
    try
    {
        //Authenticate the server with the loaded certificate
        await sslStream.AuthenticateAsServerAsync(serverCertificate, false, false);
        var buffer1 = new byte[1024];
        var bytesRead1 = await sslStream.ReadAsync(buffer1, 0, buffer1.Length);
        var content1 = Encoding.ASCII.GetString(buffer1, 0, bytesRead1);


        var buffer = new byte[1024];
        var bytesRead = await clientStream.ReadAsync(buffer, 0, buffer.Length);
        var content = Encoding.ASCII.GetString(buffer, 0, bytesRead);

I also tried reading the NetworkStream and putting the data into a MemoryStream and putting that into the SslStream, which does not work.


Solution

  • If I understand correctly: your code is the proxy server and is performing TLS offload, i.e. the outer client talks to your proxy over TLS, and your proxy talks to the internal node without TLS over a separate socket. If that (in particular the directionality) is correct, then the SslStream is already doing everything you want here; you can forget about clientStream - you should never need to read from it (i.e. your final clientStream.ReadAsync is incorrect) - it is entirely wrapped by sslStream, so that sslStream.ReadAsync is already doing the right thing. You should just need to WriteAsync the payload you read from sslStream to some other socket (not shown in your code). In a completely separate IO loop (you can't assume request/response if you're a proxy), you need to be doing the exact same thing in the other direction, i.e. reading from the inner socket and writing back to sslStream, using entirely separate buffers etc.

    However! To be honest: there's a huge difference between "doing this" and "doing this well", with threading, buffer-management, security, quotas, back-pressure, etc all major concerns. There are a range of existing tools that already do this; unless you absolutely know what you're doing and why, it would be better to defer to them.

    As a minor note: do not parse the data as ASCII - just read and write bytes. Best case: the data actually is ASCII and all you've done is add a decode/encode step for no reason. However, far more likely: you've irretrievably corrupted the data because it isn't ASCII.


    If the onward connection is also TLS, then you have a choice; a blind proxy can just forward all the bytes in both directions, without any TLS involvement at all - the traffic is all completely opaque to the proxy. This is the simplest setup, and most efficient. If you want a separate TLS connection to the inner endpoint, then you'll need a second SslStream, typically acting as a client. The overall loop doesn't change - you still just read from the the one SslStream and write to the other, and let each SslStream do their job - however, under the hood this will involve decrypt and encrypt respectively (using different keys), so there will be some processing overhead.