views:

321

answers:

0

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);
        }
    }