开发者

How to tunnel an SSL connection correctly between two NetworkStreams?

HttpWebRequest doesn't support SOCKS proxies, so after a long research I've determined, that the best way to add SOCKS support for WebRequest and WebClient (without reinventing the wheel, like SocksHttpWebRequest does) was to create a temporary HTTP proxy which forwards any incoming request to the SOCKS proxy.

Forwarding an HTTP request is easy, and works like charm.

Forwarding an HTTPS request should be theoretically easy too:

  1. The HttpWebRequest connects to our HTTP proxy, and sends the following:

    CONNECT google.com:443 HTTP/1.0
    Host: google.com
    
  2. We connect to google.com:443 through the SOCKS proxy, and then send back the following to the client:

    HTTP/1.0 200 Tunnel established
    
  3. According to this document, at this point, we copy all the bytes between the streams, and when one closes the connection, we close the other one too.

However, copying from one NetworkStream to another doesn't seem to work like I expected. At one point, Read() seems to hang for no apparent reason. If I set timeouts, regardless how big ones, I get certificate errors.

The stripped down version of the code I'm using right now:

/// <summary>
/// In theory, forwards any incoming bytes from one stream to another.
/// </summary>
/// <param name="httpProxyStream">The HTTP proxy, which we opened for <code>HttpWebRequest</code>.</param>
/// <param name="socksProxyStream">The SOCKS proxy, which we connect to and forward the traffic from the HTTP proxy.</param>
private void TunnelRequest(NetworkStream httpProxyStream, NetworkStream socksProxyStream)
{
    while (true)
    {
        try
        {
            if (httpProxyStream.DataAvailable)
            {
                CopyStreamToStream(httpProxyStream, socksProxyStream);
            }

            if (socksProxyStream.DataAvailable)
            {
                CopyStreamToStream(socksProxyStream, httpProxyStream);
            }
        }
        catch
        {
            break;
        }
    }
}

/// <summary>
/// Copies the first stream's content from the current position to the second stream.
/// </summary>
/// <param name="source">The source stream.</param>
/// <param name="destionation">The destionation stream.</param>
/// <param name="flush">if set to <c>true</c>, <c>Flush()</c> will be called on the destination stream after finish.</param>
/// <param name="bufferLength">Length of the buffer.</param>
public static void CopyStreamToStream(Stream source, Stream destionation, bool flush = true, int bufferLength = 4096)
{
    var buffer = new byte[bufferLength];

    while (true)
    {
        int i;

        try
        {
            i = source.Read(buffer, 0, bufferLength);
        }
        catch
        {
            break;
        }

        if (i == 0)
        {
            break;
        }

        destionati开发者_StackOverflow社区on.Write(buffer, 0, i);
    }

    if (flush)
    {
        destionation.Flush();
    }
}

The full class is available here: HttpToSocks.cs

I'm stuck on this piece of code for days now. The task is so simple, yet it just won't work... Please help me regain my sanity.

EDIT: I am aware that the while(true) is not my greatest line, but after lots of variations, this remained in the code to make sure it won't exit prematurely, and that's why I'm getting certificate errors.

EDIT 2: The problem has been fixed. The full class is available in the git repository, if anyone's interested.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜