HttpWebRequest
не поддерживает прокси-серверы SOCKS, поэтому после долгих исследований я определил, что лучший способ добавить поддержку SOCKS для WebRequest
и WebClient
(без повторного изобретения колеса, как SocksHttpWebRequest делает) создать временный прокси-сервер HTTP, который перенаправляет любой входящий запрос на прокси-сервер SOCKS.
Пересылка HTTP-запроса проста и работает как шарм.
Пересылка запроса HTTPS также должна быть теоретически простой:
HttpWebRequest
подключается к нашему HTTP-прокси и отправляет следующее:
CONNECT google.com:443 HTTP/1.0
Host: google.com
Мы подключаемся к google.com:443
через прокси-сервер SOCKS, а затем отправляем клиенту следующее:
HTTP/1.0 200 Tunnel established
Согласно этого документа , в этот момент мы копируем все байты между потоками, и когда один закрывает соединение, мы закрываем и другой.
Однако копирование из одного 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 , если кому-то интересно.