Мы используем TcpClient и TcpListener для связи между клиентским и серверным приложением.В некоторых случаях мы должны поддерживать подключение через прокси-сервер, и поэтому мы успешно реализовали «туннельный» подход из этой статьи .
Недавно мы «улучшили» наше общение, чтобы использоватьSslStream вместо отправки незашифрованных данных через Интернет, однако, похоже, это нарушило туннелирование прокси.
Сокращенная версия нашего кода выглядит следующим образом:
private bool Connect()
{
var uriBuilder = new UriBuilder
{
Scheme = Uri.UriSchemeHttp,
Host = _proxyServerHost,
Port = _proxyServerPort
};
var proxyUri = uriBuilder.Uri;
var request = WebRequest.Create(
"http://" + _targetHost + ":" + _targetPort);
var webProxy = new WebProxy(proxyUri);
request.Proxy = webProxy;
request.Method = "CONNECT";
if (_proxyUserName == null)
{
var credentials = new NetworkCredential(
_proxyUserName, _proxyPassword);
webProxy.Credentials = credentials;
}
HttpWebResponse response;
logger.InfoExt("{83CAAA66-3004-4C13-8F9A-5B1E23063E53}", $"Sending CONNECT command to the proxy server");
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (Exception ex)
{
logger.Error(ex, "{DF8498B5-A4CC-49ED-A928-BCB6D7D087D6}", "Error getting HTTP Response");
return false;
}
if (response.StatusCode != HttpStatusCode.OK)
{
logger.ErrorExt("{2D4040D6-9477-479E-B25D-2DBB58273265}",
$"Not able to connect to proxy server. Response code: {response.StatusCode}");
ConnectionStatus = eSocketConnectionStatus.NotConnected;
return false;
}
logger.InfoExt("{1E522215-3DB0-4ED7-8E6A-E2AD1AFD82D9}", $"Connected to the proxy server");
var responseStream = response.GetResponseStream();
const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance;
var rsType = responseStream.GetType();
var connectionProperty = rsType.GetProperty("Connection", Flags);
var connection = connectionProperty.GetValue(responseStream, null);
var connectionType = connection.GetType();
var networkStreamProperty = connectionType.GetProperty("NetworkStream", Flags);
var networkStream = networkStreamProperty.GetValue(connection, null);
var nsType = networkStream.GetType();
var socketProperty = nsType.GetProperty("Socket", Flags);
var socket = (Socket)socketProperty.GetValue(networkStream, null);
_tcpClient = new TcpClient { Client = socket };
var sslStream = new SslStream(_tcpClient.GetStream(), true,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
sslStream.AuthenticateAsClient(_targetHost);
}
catch (Exception ex)
{
logger.Error(ex, "{CA1D5BDB-B85D-41AE-B63D-1F4804765FC6}", "Error authenticating SSL stream");
_tcpClient.Close();
ConnectionStatus = eSocketConnectionStatus.ConnectionFailedToInitiate;
return false;
}
_tcpClientStream = sslStream;
pfnCallBack = new AsyncCallback(OnDataRecevied);
_tcpClientStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, pfnCallBack, null);
return true;
}
private void OnDataReceived(IAsyncResult asyn)
{
var iRx = _tcpClientStream.EndRead(asyn);
if (iRx <= 0)
{
// ERROR so disconnect
return;
}
}
Проблема, с которой мы сталкиваемсяв том, что когда мы вызываем BeginRead, обратный вызов немедленно вызывается, и EndRead возвращает 0 байтов, что в соответствии с документацией MSDN означает, что сокет был закрыт, поэтому мы завершаем наше соединение.
Если вместо использования
_tcpClientStream = sslStream;
мы используем
_tcpClientStream = _tcpClient.GetStream();
(т. Е. Мы не используем SSL), тогда код работает нормально, и у нас нет проблем.
Может кто-нибудь предложить какой-либо советпо этой теме?
- Есть ли что-то принципиально ошибочное в идее использования точно такого же подхода и просто открытияпоток SSL?
- Я установил Fiddler на свой компьютер, и он, кажется, работает при использовании прокси-сервера Fiddler, однако, когда программное обеспечение запускается через «правильный» прокси-сервер пользователя, что-то не работает
- IP-адрес и порт сервера были внесены в белый список прокси-сервером, и поэтому их не следует блокировать
- Является ли Fiddler «лучшим способом» настроить тестовый прокси-сервер для подтверждения правильности нашей реализации?
Блок кода, который «вдохновил» наш подход, кажется довольно популярным (обнаруживается в многочисленных репозиториях Github, других вопросах Stackoverflow и т. Д.), Поэтому я совершенно уверен, что это правильный подход ввообще?