How to forward HTTPS traffic from a SOCKS proxy to

2019-04-15 11:46发布

问题:

I have written a SOCKS proxy which works with both HTTP and HTTPS traffic if chaining is turned off.

If chaining is turned on and the forwarding host and port belong to a filtering HTTP proxy then only HTTP traffic can flow. HTTPS traffic does not flow and reports an SSL error.

Note that the HTTP proxy does handle HTTPS traffic when the request is coming direct from the browser and not from the SOCKS server.

As an example if I make a request to https://www.google.com the following occurs:

1) Client sends a SOCKS 5 greeting to let us know which auth methods are acceptable.

2) Server responds with NO_AUTH

3) Client sends a connection request (which includes the intended destination host and port).

4) The server creates and connects a socket to the HTTP proxy server and responds to the client with GRANTED

5) The SOCKS server then receives the following request (the invisible/control characters have all been replaced with their character codes so you can see what's going on):

\u0016\u0003\u0001\u0000Ñ\u0001\u0000\u0000Í\u0003\u0003áp¥@Ia¹\u0001„Ä\u0006 É;š‰‰4\u001dýà•J>Ü6¢Þ\fö\u001c%\u0000\u0000(À+À/\u0000žÌ\u0014Ì\u0013À\nÀ\tÀ\u0013À\u0014À\u0007À\u0011\u00003\u00002\u00009\u0000œ\u0000/\u00005\u0000\n\u0000\u0005\u0000\u0004\u0001\u0000\u0000|\u0000\u0000\u0000\u0013\u0000\u0011\u0000\u0000\u000ewww.google.comÿ\u0001\u0000\u0001\u0000\u0000\n\u0000\b\u0000\u0006\u0000\u0017\u0000\u0018\u0000\u0019\u0000\u000b\u0000\u0002\u0001\u0000\u0000#\u0000\u00003t\u0000\u0000\u0000\u0010\u0000\u001b\u0000\u0019\u0006spdy/3\bspdy/3.1\bhttp/1.1uP\u0000\u0000\u0000\u0005\u0000\u0005\u0001\u0000\u0000\u0000\u0000\u0000\u0012\u0000\u0000\u0000\r\u0000\u0012\u0000\u0010\u0004\u0001\u0005\u0001\u0002\u0001\u0004\u0003\u0005\u0003\u0002\u0003\u0004\u0002\u0002\u0002

As you can see, its completely unreadable, but we already know where the user intends to go from the initial SOCKS connection message (step 3) so we can create and issue the following connect request:

CONNECT www.google.com:443 HTTP/1.1\r\nUser-Agent: MySocksServer\r\nProxy-Connection: keep-alive\r\nHost: www.google.com\r\n\r\n

6) This newly constructed CONNECT is sent to the HTTP proxy that we are chaining to, this proxy checks against its filter rules and responds with:

HTTP/1.1 200 Connection Established\r\nVia: 1.1 HTTPserverName\r\nX-WebMarshal-RequestID: AN_ID_STRING\r\n\r\n

7) This is received in our SOCKS server and is forwarded (unmodified) to the client. In my debugging I am monitoring the sockets directly before the send request and can see that the client socket is connected.

8) The next event raised is an error, with the error, SOCKET_NOT_CONNECTED, running another check on the sockets confirms that the client socket is indeed no longer connected.

Why would my Socket be getting closed between steps 7 and 8 here? Have I not followed the protocol correctly? I can't see what I am missing. I guess I am handling the CONNECT method wrong in some way?

If I do not modify the request received by the SOCKS server (i.e. convert it into a CONNECT request) and instead forward on the unreadable data direct to the HTTP proxy then the Logs for the HTTP proxy show either:

Badly formated request: \u0016\u0003\u001
Bad request received.

OR

Failed to read request: Client closed connection. (0)
1Request took 0 ms + 23 ms idle time

回答1:

6) This newly constructed CONNECT is sent to the HTTP proxy ... this proxy ... responds with:

7) This is received in our SOCKS server and is forwarded (unmodified) to the client...

This is wrong. You generate the CONNECT request in your SOCKS proxy and therefore you should keep the response to this request to yourself and not forward it to the client. What you should do:

  • If you receive the start of the SSL handshake from the client ("\x16\x03... ") you should buffer it.
  • Then you create the CONNECT request and send it to the proxy. The Host header and Proxy-Connection headers have no meaning with CONNECT so you don't need to add them.
  • Read the response from the proxy to the CONNECT request. If status code is not 200 something is wrong and you should close the connection to the client. There is no easy way to transfer the error information to the client.
  • If status code is 200 forward the buffered ClientHello from the client to the server through the proxy and from then on forward everything between client and server (through the proxy tunnel).


回答2:

The correct sequence is:

  1. client connects to SOCKS proxy, authenticates as needed.

  2. client sends a SOCKS connect request to create a tunnel to www.google.com:443.

  3. SOCKS proxy connects to HTTP proxy

  4. SOCKS proxy sends an HTTP CONNECT request to create a tunnel to www.google.com:443.

  5. SOCKS proxy receives a reply from HTTP proxy.

  6. SOCKS proxy sends an appropriate SOCKS reply to client.

  7. If HTTP proxy was successful, pass unmodified data between client and HTTP proxy until one of them disconnects.

  8. close the client connection and the HTTP proxy connection.

When you chain proxies, you have to negotiate the tunnels before you can then start passing application data through them. Do not send a tunnel reply to the client until the next proxy replies with its tunnel status first.