简体   繁体   中英

Transparent proxy - from port 80 to 443

I'm trying to develop a transparent proxy which forwards unsecured http traffic from the clients as secured https traffic to the server, and back again. To better illustrate my point, take a look at the following image.

丰富的图片

Let's suppose that for various reasons the clients will be using HTTP only, they can't use port 443 for HTTPS. Since some servers don't accept traffic from port 80, my proxy needs to reroute them to port 443. This is a possible scenario:

  1. Receive data from client which are headed to port 80 towards www.google.com
  2. Initialize connection with https://www.google.com to port 443 (Do the handshake and so on)
  3. Encrypt data from client and send them to https://www.google.com to port 443.
  4. Receive response from https://www.google.com , decrypt them and send them back to the client to port 80.

Since this is a transparent proxy, the clients (in my case lots of them) shouldn't need any extra configuration. The network is already configured so that their traffic go through my node. Currently my node simply reroutes the data and blocks some if they contain viruses. This is done using WinPcap to get access to low networking layers but I'm willing to change my approach if it's too hard to be done using raw packets (mainly concerned about the handshake).

What I've tried: Note: www.google.com could be any site on the web. It's merely used as an example.

  1. Iridium's suggestion. This doesn't work for the reason that TcpListener only accepts a new connection if another application uses a TcpClient to connect to it. Since this is a transparent proxy, it doesn't work.
  2. Using HttpListener instead. However, it seems that this doesn't work since it only accepts connections to my own IP (not www.google.com).
  3. Using HttpListener as before but this time I'm forwarding packets to my own IP so that HttpListener accepts the connection. For some reason, this doesn't seem to work (Inspected through wireshark and TCP SYN packets keep re-sending, not sure why or how to fix it).
  4. Using SslStream to connect to https://www.google.com , then getting the contents from the raw packets received from the clients and writing them to the stream. This doesn't work since SslStream handles TCP packets (Such as ACK or SYN) on its own. The stream expects only Http requests. Another reason why it doesn't work is because I can't read the contents of TCP packets from the stream, only the contents of HTTP responses (So the client is sat there waiting for an ACK).
  5. Forwarding TCP packets from the client as they are to port 443 and from the server to port 80 (Since only HTTP requests and responses are encrypted with SSL, it wouldn't make a difference) and using the HttpRequest class to make all the http requests and responses (Since the class handles the handshake on its own). This doesn't work because the ACKs are wrong on both sides.

What would be the best way to go into developing such a proxy?

Edit: Is there ANY way that TcpListener or HttpListener can act as a transparent proxy? (Without configuration on the clients' computers). When exactly does HttpListener recognize that a client is trying to connect?

I don't really understand why you need to read the encrypted data from the SslStream . If I'm reading your description correctly, you should simply need to:

  • Wait for a client connection
  • When a client connects, connect to the server
  • Wrap the server connection's NetworkStream in an SslStream and AuthenticateAsClient
  • Once authenticated, in parallel:
    • Read data from the client and write it to the SslStream
    • Read data from the SslStream and write it to the client

I don't see anywhere in this process that you'd need to see the encrypted data from the SslStream .

The following is a very basic sample (though completely untested):

static void Main(string[] args)
{
    var listener = new TcpListener(IPAddress.Any, 11180);
    var clientConnection = listener.AcceptTcpClient();
    // When we get here, the client has connected, initiate the server connection
    var serverConnection = new TcpClient("your.server.name", 443);
    var serverStream = serverConnection.GetStream();
    var secureStream = new SslStream(serverStream);
    secureStream.AuthenticateAsClient("your.server.name");
    ConnectStreams(clientConnection.GetStream(), secureStream);
}

private static void ConnectStreams(Stream streamA, Stream streamB)
{
    ForwardStream(streamA, streamB, new byte[1024]);
    ForwardStream(streamB, streamA, new byte[1024]);
}

private static void ForwardStream(Stream source, Stream destination, byte[] buffer)
{
    source.BeginRead(buffer, 0, buffer.Length, r => Forward(source, destination, r, buffer), null);
}

private static void Forward(Stream source, Stream destination, IAsyncResult asyncResult, byte[] buffer)
{
    var bytesRead = source.EndRead(asyncResult);
    if (bytesRead == 0)
    {
        destination.Close();
        return;
    }
    destination.Write(buffer, 0, bytesRead);
    ForwardStream(source, destination, buffer);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM