如何在两个 NetworkStream 之间正确建立 SSL 连接隧道?

发布于 2024-11-28 18:06:07 字数 3377 浏览 1 评论 0原文

HttpWebRequest 不支持 SOCKS 代理,因此经过长时间的研究,我确定,为 WebRequestWebClient 添加 SOCKS 支持的最佳方法> (无需重新发明轮子,例如 SocksHttpWebRequest 的作用是创建一个临时 HTTP 代理,它将任何传入请求转发到 SOCKS 代理。

转发 HTTP 请求非常简单,而且非常有效。

转发 HTTPS 请求理论上也应该很容易:

  1. HttpWebRequest 连接到我们的 HTTP 代理,并发送以下内容:

    连接 google.com:443 HTTP/1.0
    主办方:google.com
    
  2. 我们通过 SOCKS 代理连接到 google.com:443 ,然后将以下内容发送回客户端:

    HTTP/1.0 200 隧道已建立
    
  3. 根据 此文档,此时,我们复制流之间的所有字节,当关闭连接时,我们关闭另一个

但是,从一个 NetworkStream 复制到另一个 NetworkStream 似乎并不像我预期的那样工作。在某一时刻,Read() 似乎没有明显原因而挂起。如果我设置超时,无论超时有多大,我都会收到证书错误。

我现在使用的代码的精简版本:

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

        destionation.Write(buffer, 0, i);
    }

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

完整的类可以在这里找到:HttpToSocks.cs

我被卡住了这段代码已经研究了好几天了。任务如此简单,却根本行不通……请帮助我恢复理智。

编辑:我知道while(true)不是我最棒的一行,但经过很多变化后,它保留在代码中以确保它不会退出过早,这就是我收到证书错误的原因。

编辑2:问题已得到解决。完整的课程可在 git 存储库中获取,如果有人有兴趣的话。

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

        destionation.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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文