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
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:
The correct sequence is:
client connects to SOCKS proxy, authenticates as needed.
client sends a SOCKS connect request to create a tunnel to
www.google.com:443
.SOCKS proxy connects to HTTP proxy
SOCKS proxy sends an HTTP
CONNECT
request to create a tunnel towww.google.com:443
.SOCKS proxy receives a reply from HTTP proxy.
SOCKS proxy sends an appropriate SOCKS reply to client.
If HTTP proxy was successful, pass unmodified data between client and HTTP proxy until one of them disconnects.
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.