I'm trying to build a service that will forward HTTP requests from agents like a browser to the Tor service. Problem is, the Tor service only accepts SOCKS4a connections.
So my solution is to listen for HTTP requests, get the URL they're requesting, and make a request via Tor with the help of the Starksoft.Net.Proxy library. Then return the response. The library kind of works, but I'm not happy. It returns HTTP headers with the response and it can't handle images. So the responses are messed up.
How could I improve my code? I'm very new to network programming. Sorry for the long example.
public AnonymiserService(ILogger logger)
{
try
{
_logger = logger;
_logger.Log("Listening on port {0}...", Properties.Settings.Default.ListeningPort);
StartListener(new string[] { string.Format("http://*:{0}/", Properties.Settings.Default.ListeningPort) });
}
catch (Exception ex)
{
_logger.LogError("Exception!", ex);
}
}
private void StartListener(string[] prefixes)
{
if (!HttpListener.IsSupported)
{
_logger.LogError("HttpListener isn't supported on this machine!");
return;
}
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
listener.Prefixes.Add(s);
while (true)
{
listener.Start();
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
result.AsyncWaitHandle.WaitOne();
}
}
private void ListenerCallback(IAsyncResult result)
{
try
{
// Get HTTP request
HttpListener listener = (HttpListener)result.AsyncState;
HttpListenerContext context = listener.EndGetContext(result);
_logger.Log("Retrieving [{0}]", context.Request.RawUrl);
// Create connection
// Use Tor as proxy
IProxyClient proxyClient = new Socks4aProxyClient("localhost", 9050);
TcpClient tcpClient = proxyClient.CreateConnection(context.Request.UserHostName, 80);
// Create message
// Need to set Connection: close to close the connection as soon as it's done
byte[] data = Encoding.UTF8.GetBytes(String.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\nConnection: close\r\n\r\n", context.Request.Url.PathAndQuery, context.Request.UserHostName));
// Send message
NetworkStream ns = tcpClient.GetStream();
ns.Write(data, 0, data.Length);
// Pass on HTTP response
HttpListenerResponse responseOut = context.Response;
if (ns.CanRead)
{
byte[] buffer = new byte[32768];
int read = 0;
string responseString = string.Empty;
// Read response
while ((read = ns.Read(buffer, 0, buffer.Length)) > 0)
{
responseString += Encoding.UTF8.GetString(buffer, 0, read);
}
// Remove headers
if (responseString.IndexOf("HTTP/1.1 200 OK") > -1)
responseString = responseString.Substring(responseString.IndexOf("\r\n\r\n"));
// Forward response
byte[] byteArray = Encoding.UTF8.GetBytes(responseString);
responseOut.OutputStream.Write(byteArray, 0, byteArray.Length);
}
// Close streams
responseOut.OutputStream.Close();
ns.Close();
// Close connection
tcpClient.Close();
_logger.Log("Retrieved [{0}]", context.Request.RawUrl);
}
catch (Exception ex)
{
_logger.LogError("Exception in ListenerCallback!", ex);
}
}