Как правильно туннелировать SSL-соединение между двумя NetworkStreams? - PullRequest
3 голосов
/ 09 августа 2011

HttpWebRequest не поддерживает прокси-серверы SOCKS, поэтому после долгих исследований я определил, что лучший способ добавить поддержку SOCKS для WebRequest и WebClient (без повторного изобретения колеса, как SocksHttpWebRequest делает) создать временный прокси-сервер HTTP, который перенаправляет любой входящий запрос на прокси-сервер SOCKS.

Пересылка HTTP-запроса проста и работает как шарм.

Пересылка запроса HTTPS также должна быть теоретически простой:

  1. HttpWebRequest подключается к нашему HTTP-прокси и отправляет следующее:

    CONNECT google.com:443 HTTP/1.0
    Host: google.com
    
  2. Мы подключаемся к google.com:443 через прокси-сервер SOCKS, а затем отправляем клиенту следующее:

    HTTP/1.0 200 Tunnel established
    
  3. Согласно этого документа , в этот момент мы копируем все байты между потоками, и когда один закрывает соединение, мы закрываем и другой.

Однако копирование из одного 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 , если кому-то интересно.

...