如何在两个 NetworkStream 之间正确建立 SSL 连接隧道?
HttpWebRequest
不支持 SOCKS 代理,因此经过长时间的研究,我确定,为 WebRequest
和 WebClient
添加 SOCKS 支持的最佳方法> (无需重新发明轮子,例如 SocksHttpWebRequest 的作用是创建一个临时 HTTP 代理,它将任何传入请求转发到 SOCKS 代理。
转发 HTTP 请求非常简单,而且非常有效。
转发 HTTPS 请求理论上也应该很容易:
HttpWebRequest 连接到我们的 HTTP 代理,并发送以下内容:
连接 google.com:443 HTTP/1.0 主办方:google.com
我们通过 SOCKS 代理连接到
google.com:443
,然后将以下内容发送回客户端:HTTP/1.0 200 隧道已建立
根据 此文档,此时,我们复制流之间的所有字节,当关闭连接时,我们关闭另一个
但是,从一个 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:
The
HttpWebRequest
connects to our HTTP proxy, and sends the following:CONNECT google.com:443 HTTP/1.0 Host: google.com
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
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论