[*] идентифицировать клиентов: < /strong> на основе параметров строки соединения. (Я ограничен идентификацией на стороне клиента только через строку подключения).
[*] запросы маршрутов на основе содержания и времени: [/b] для определенных запросов (идентифицированные по содержанию и время выполнения), прокси должен возвращать результаты из хранимого файла вместо того, чтобы нажимать на фактическую базу данных. Серверные рукопожатия TLS и выполняют необработанные переадресации байтов. Тем не менее, я сталкиваюсь с постоянными проблемами при подключении к прокси с .NET SQLConnection или SQLCMD -N -C (принуждение шифрования).
Проблема:
При использовании клиента SQL с Encrypt = true в строке подключения для подключения через прокси -сервер, клиент постоянно не позволяет установить соединение и бросает Следующая ошибка: < /p>
---> system.io.ioexception: получил неожиданный EOF или 0 байтов от транспортного потока. < /p>
< /blockquote>
Прокси -журналы последовательно показывают: < /p>
Код: Выделить всё
Minimal TLS Proxy listening on port 14330
HandleClientAsync: Client connection accepted.
HandleClientAsync: Forwarding FULL Server Prelogin Response (Hex): ...
HandleClientAsync: FULL Server prelogin response forwarded to client.
HandleClientAsync: Starting client TLS handshake.
TLS handshake failed: Received an unexpected EOF or 0 bytes from the transport stream.
< /code>
Интересно, [b]openssl s_client -connect 127.0.0.1:14330 -tls1_2Что я 've пытался (шаги отладки): < /strong> < /p>
Я предпринял обширные усилия по отладки, включая: < /p>
- непосредственно пересылав полный ответ предварительного ответа сервера на клиент. li>
упрощенный tdstlsstream к необработанному прохождению. > - явно устанавливает sslprotocols.tls12 для рукопожатия клиента и сервера TLS.
- Алгоритм отключения Нагле на сокетах. Захваты сетевого трафика.
- отлично работает с сервером TLS Proxy. [/b]
Код: Выделить всё
openssl s_client - клиенты SQL (, sqlcmd -n -c ) постоянно не выполняет рукопожатие TLS через прокси. Клиент рано прерывает рукопожатие или соединение TLS. для саморегистрированных Certs). [/b]
Код: Выделить всё
SqlConnection - System.indexoutofrangeException , которая первоначально наблюдалась в клиенте, была разрешена путем пересылки полного сервера-прелюгина Ответ, но сбой рукопожатия TLS сохраняется.
(minimaltlsproxy.cs - метод handleclientasync):
Код: Выделить всё
private async Task HandleClientAsync(TcpClient client)
{
FileLogger.Log("HandleClientAsync: Client connection accepted.");
using (client)
{
client.NoDelay = true;
NetworkStream clientNetStream = client.GetStream();
SslStream clientSslStream = new SslStream(clientNetStream, false);
SslStream sslUpstreamStream = null;
// Start Client TLS Handshake IMMEDIATELY
try
{
FileLogger.Log("HandleClientAsync: Starting client TLS handshake (EARLY START).");
DateTime clientHandshakeStart = DateTime.Now;
await clientSslStream.AuthenticateAsServerAsync(_serverCertificate, false, SslProtocols.Tls12, false);
TimeSpan clientHandshakeDuration = DateTime.Now - clientHandshakeStart;
Console.WriteLine($"Client TLS handshake completed in {clientHandshakeDuration.TotalMilliseconds} ms.");
FileLogger.Log($"Client TLS handshake completed in {clientHandshakeDuration.TotalMilliseconds} ms.");
FileLogger.Log("HandleClientAsync: Client TLS handshake completed.");
}
catch (Exception ex)
{
Console.WriteLine("TLS handshake failed: " + ex.Message);
FileLogger.Log("TLS handshake failed: " + ex.Message);
return;
}
byte[] clientPreloginPacket = await ReadTdsPacketAsync(clientNetStream);
if (clientPreloginPacket != null)
{
TcpClient upstreamClient = new TcpClient();
Stream upstreamStream = null;
try
{
await upstreamClient.ConnectAsync(_targetServer, _targetPort);
upstreamStream = upstreamClient.GetStream();
upstreamClient.NoDelay = true;
await upstreamStream.WriteAsync(clientPreloginPacket, 0, clientPreloginPacket.Length);
await upstreamStream.FlushAsync();
byte[] serverPreloginPacket = await ReadTdsPacketAsync(upstreamStream);
if (serverPreloginPacket != null)
{
FileLogger.Log($"HandleClientAsync: Forwarding FULL Server Prelogin Response (Hex): {BitConverter.ToString(serverPreloginPacket).Replace('-', ' ')}");
await clientNetStream.WriteAsync(serverPreloginPacket, 0, serverPreloginPacket.Length);
await clientNetStream.FlushAsync();
FileLogger.Log("HandleClientAsync: FULL Server prelogin response forwarded to client.");
}
}
finally
{
upstreamClient?.Close();
upstreamStream?.Dispose();
}
}
sslUpstreamStream = null;
NetworkStream upstreamNetStream = null;
TcpClient upstreamClientForForwarding = new TcpClient();
try
{
await upstreamClientForForwarding.ConnectAsync(_targetServer, _targetPort);
upstreamNetStream = upstreamClientForForwarding.GetStream();
sslUpstreamStream = new SslStream(upstreamNetStream, false, (sender, certificate, chain, sslPolicyErrors) => true);
DateTime upstreamHandshakeStart = DateTime.Now;
await sslUpstreamStream.AuthenticateAsClientAsync(_targetServer, null, SslProtocols.Tls12, false);
TimeSpan upstreamHandshakeDuration = DateTime.Now - upstreamHandshakeStart;
Console.WriteLine($"Upstream TLS handshake completed in {upstreamHandshakeDuration.TotalMilliseconds} ms.");
FileLogger.Log($"Upstream TLS handshake completed in {upstreamHandshakeDuration.TotalMilliseconds} ms.");
FileLogger.Log("HandleClientAsync: Upstream TLS handshake completed.");
// Begin bidirectional forwarding - RAW BYTE FORWARDING
Task clientToServer = ForwardTrafficAsync(clientSslStream, sslUpstreamStream, "CLIENT -> SERVER");
Task serverToClient = ForwardTrafficAsync(upstreamSslStream, clientSslStream, "SERVER -> CLIENT");
await Task.WhenAll(clientToServer, serverToClient);
}
catch (Exception ex)
{
Console.WriteLine("Upstream connection/TLS failed: " + ex.Message);
FileLogger.Log("Upstream connection/TLS failed: " + ex.Message);
}
finally
{
upstreamClientForForwarding?.Close();
upstreamNetStream?.Dispose();
sslUpstreamStream?.Dispose();
}
}
}
Код: Выделить всё
///
/// Forwards traffic between two streams (Simplified version - basic byte forwarding) - WITH GRANULAR TIMING LOGS.
///
private async Task ForwardTrafficAsync(Stream source, Stream destination, string direction)
{
byte[] buffer = new byte[4096];
try
{
while (true)
{
DateTime readStart = DateTime.Now; // Timestamp before ReadAsync
int bytesRead = await source.ReadAsync(buffer, 0, buffer.Length);
TimeSpan readDuration = DateTime.Now - readStart; // Duration of ReadAsync
FileLogger.Log($"{direction} - ReadAsync - Read {bytesRead} bytes in {readDuration.TotalMilliseconds} ms."); // Log ReadAsync timing
if (bytesRead == 0)
{
string msg = $"{direction} connection closed by remote endpoint.";
Console.WriteLine(msg);
FileLogger.Log(msg);
break;
}
DateTime writeStart = DateTime.Now; // Timestamp before WriteAsync
await destination.WriteAsync(buffer, 0, bytesRead);
DateTime flushStart = DateTime.Now; // Timestamp before FlushAsync
await destination.FlushAsync();
TimeSpan writeDuration = DateTime.Now - writeStart; // Duration of WriteAsync+FlushAsync
FileLogger.Log($"{direction} - WriteAsync+FlushAsync - Wrote {bytesRead} bytes in {writeDuration.TotalMilliseconds} ms."); // Log WriteAsync+FlushAsync timing
}
}
catch (Exception ex)
{
string err = $"Forwarding error ({direction}): {ex.Message}";
Console.WriteLine(err);
FileLogger.Log(err);
}
}
Код: Выделить всё
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SqlProxyExample
{
///
/// Custom stream that strips the TDS header to expose raw TLS records.
/// This implementation assumes one TLS record per TDS packet.
/// For writing, it encapsulates TLS bytes into a TDS packet.
/// **SIMPLIFIED VERSION - RAW PASS-THROUGH FOR TESTING - WITH ENHANCED LOGGING**
///
public class TdsTlsStream : Stream
{
private readonly Stream _baseStream;
public TdsTlsStream(Stream baseStream)
{
_baseStream = baseStream;
FileLogger.Log("TdsTlsStream: Using SIMPLIFIED RAW PASS-THROUGH VERSION with ENHANCED LOGGING for testing."); // Added log
}
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length; // Or throw new NotSupportedException();
public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } // Or throw new NotSupportedException();
public override void Flush() => _baseStream.Flush();
public override int Read(byte[] buffer, int offset, int count)
{
FileLogger.Log($"TdsTlsStream.Read (SIMPLIFIED - LOGGED): Reading {count} bytes from base stream.");
DateTime startTime = DateTime.Now; // Log start time
int read = _baseStream.Read(buffer, offset, count);
TimeSpan duration = DateTime.Now - startTime; // Log duration
FileLogger.Log($"TdsTlsStream.Read (SIMPLIFIED - LOGGED): Read {read} bytes in {duration.TotalMilliseconds} ms.");
return read;
}
public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
FileLogger.Log($"TdsTlsStream.ReadAsync (SIMPLIFIED - LOGGED): Reading {count} bytes from base stream.");
DateTime startTime = DateTime.Now; // Log start time
int read = await _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
TimeSpan duration = DateTime.Now - startTime; // Log duration
FileLogger.Log($"TdsTlsStream.ReadAsync (SIMPLIFIED - LOGGED): Read {read} bytes in {duration.TotalMilliseconds} ms.");
return read;
}
public override void Write(byte[] buffer, int offset, int count)
{
FileLogger.Log($"TdsTlsStream.Write (SIMPLIFIED - LOGGED): Writing {count} bytes to base stream.");
DateTime startTime = DateTime.Now; // Log start time
_baseStream.Write(buffer, offset, count);
TimeSpan duration = DateTime.Now - startTime; // Log duration
FileLogger.Log($"TdsTlsStream.Write (SIMPLIFIED - LOGGED): Written {count} bytes in {duration.TotalMilliseconds} ms.");
}
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
FileLogger.Log($"TdsTlsStream.WriteAsync (SIMPLIFIED - LOGGED): Writing {count} bytes to base stream.");
DateTime startTime = DateTime.Now; // Log start time
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
TimeSpan duration = DateTime.Now - startTime; // Log duration
FileLogger.Log($"TdsTlsStream.WriteAsync (SIMPLIFIED - LOGGED): Written {count} bytes in {duration.TotalMilliseconds} ms.");
}
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin); // Or throw new NotSupportedException();
public override void SetLength(long value) => _baseStream.SetLength(value); // Or throw new NotSupportedException();
}
}
Я ищу экспертные советы из сообщества переполнения стека, особенно тех, кто знаком с : < /p>
.net Networking, Sslstream, NetworkStream и асинхронное программирование. Чувствительность. > конкретные вопросы: < /strong> < /p>
Почему клиент .NET SQL (SQLConnection, sqlcmd -n -c) не удается Заполните рукопожатие TLS через прокси с «неожиданным EOF» или ошибками прерывания соединения, в то время как OpenSSL S_Client работает нормально? < /p>
< /li>
или неэффективен в минимальной логике обработки или пересылки минимального кода, которая может вызвать эти проблемы для клиентов SQL? Тонкие требования клиента .NET SQL при подключении через прокси с включенными TLS, которые я могу отсутствовать? С помощью строки подключения, маршрутизации ответов на основе запросов) есть ли альтернативные, более надежные или более простые подходы для достижения этих целей вместо создания пользовательского прокси-сервера TDS с нуля? Возможно, использование существующих решений обратного прокси или других методов сети .NET было бы более надежным? Шаги отладки, или альтернативные подходы были бы очень оценены!
Спасибо за ваше время и опыт.
Подробнее здесь: https://stackoverflow.com/questions/794 ... client-spe
Мобильная версия