.NET: Connecting a TcpClient through an HTTP proxy with authentication

November 17, 2010

While implementing proxy support in SharedSafe, we learned that HTTP proxy support in .NET does not actually support the lower level classes like TcpClient or Socket.

Disappointed by the fact that every networking client has to implement its own proxy handling, I decided to integrate a small proxy library by Starksoft that seemed to fit all requirements, but then we found out that their HTTP proxy did not support authentication.

Inspired by a hack on Stackoverflow that made the internal NetworkStream available from a HttpWebResponse, I decided to continue hacking and came up with the following solution:

Be warned, this is a preliminary hack and may not work at all:

static TcpClient connectViaHTTPProxy(
string targetHost,
int targetPort,
string httpProxyHost,
int httpProxyPort,
string proxyUserName,
string proxyPassword)
{
var uriBuilder = new UriBuilder
{
Scheme = Uri.UriSchemeHttp,
Host = httpProxyHost,
Port = httpProxyPort
};

var proxyUri = uriBuilder.Uri;

var request = WebRequest.Create(
"http://" + targetHost + ":" + targetPort);

var webProxy = new WebProxy(proxyUri);

request.Proxy = webProxy;
request.Method = "CONNECT";

var credentials = new NetworkCredential(
proxyUserName, proxyPassword);

webProxy.Credentials = credentials;

var response = request.GetResponse();

var responseStream = response.GetResponseStream();
Debug.Assert(responseStream != null);

const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance;

var rsType = responseStream.GetType();
var connectionProperty = rsType.GetProperty("Connection", Flags);

var connection = connectionProperty.GetValue(responseStream, null);
var connectionType = connection.GetType();
var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags);

var networkStream = networkStreamProperty.GetValue(connection, null);
var nsType = networkStream.GetType();
var socketProperty = nsType.GetProperty("Socket", Flags);
var socket = (Socket)socketProperty.GetValue(networkStream, null);

return new TcpClient { Client = socket };
}
 
Instead of sending a “GET” method to the proxy server, a “CONNECT” method is sent. Then, the internal NetworkStream is extracted via Reflections. And after that, the Socket is taken from the stream (a protected property) and wrapped into a TcpClient instance.
 

Some notes:

Please comment here if you find out more details, I’ll update the blog post.