From 34a7e94a7bbd33c518b856cdb311e4d96543203b Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:10:38 +0100 Subject: [PATCH 01/14] netfx, netcore: move duplicate WritePacketCache class Duplicated in netcore by a class in TdsParserSafeHandles.Windows.cs. Remove the duplicate and add to netfx. --- .../SqlClient/TdsParserStateObjectNative.cs | 2 +- .../SqlClient/TdsParserStateObjectNative.cs | 59 +++++++++++++++++++ .../SqlClient/TdsParserSafeHandles.Windows.cs | 59 ------------------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index a553b43dde..954164bd14 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -492,7 +492,7 @@ internal override void DisposePacketCache() internal override SspiContextProvider CreateSspiContextProvider() => new NativeSspiContextProvider(); - internal sealed class WritePacketCache : IDisposable + private sealed class WritePacketCache : IDisposable { private bool _disposed; private Stack _packets; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index cb85e2b9b1..ec4c1782f3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -448,5 +448,64 @@ internal override void DisposePacketCache() } internal override SspiContextProvider CreateSspiContextProvider() => new NativeSspiContextProvider(); + + private sealed class WritePacketCache : IDisposable + { + private bool _disposed; + private Stack _packets; + + public WritePacketCache() + { + _disposed = false; + _packets = new Stack(); + } + + public SNIPacket Take(SNIHandle sniHandle) + { + SNIPacket packet; + if (_packets.Count > 0) + { + // Success - reset the packet + packet = _packets.Pop(); + SniNativeWrapper.SniPacketReset(sniHandle, IoType.WRITE, packet, ConsumerNumber.SNI_Consumer_SNI); + } + else + { + // Failed to take a packet - create a new one + packet = new SNIPacket(sniHandle); + } + return packet; + } + + public void Add(SNIPacket packet) + { + if (!_disposed) + { + _packets.Push(packet); + } + else + { + // If we're disposed, then get rid of any packets added to us + packet.Dispose(); + } + } + + public void Clear() + { + while (_packets.Count > 0) + { + _packets.Pop().Dispose(); + } + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + Clear(); + } + } + } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs index ea4c46753e..749fe5a008 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs @@ -277,63 +277,4 @@ override protected bool ReleaseHandle() return true; } } - - internal sealed class WritePacketCache : IDisposable - { - private bool _disposed; - private Stack _packets; - - public WritePacketCache() - { - _disposed = false; - _packets = new Stack(); - } - - public SNIPacket Take(SNIHandle sniHandle) - { - SNIPacket packet; - if (_packets.Count > 0) - { - // Success - reset the packet - packet = _packets.Pop(); - SniNativeWrapper.SniPacketReset(sniHandle, IoType.WRITE, packet, ConsumerNumber.SNI_Consumer_SNI); - } - else - { - // Failed to take a packet - create a new one - packet = new SNIPacket(sniHandle); - } - return packet; - } - - public void Add(SNIPacket packet) - { - if (!_disposed) - { - _packets.Push(packet); - } - else - { - // If we're disposed, then get rid of any packets added to us - packet.Dispose(); - } - } - - public void Clear() - { - while (_packets.Count > 0) - { - _packets.Pop().Dispose(); - } - } - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - Clear(); - } - } - } } From b0b54eb154f6e092d432c5014c83d7b157bf0b56 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:14:01 +0100 Subject: [PATCH 02/14] netfx, netcore: sync comments and whitespace --- .../Data/SqlClient/TdsParserStateObjectNative.cs | 6 +++++- .../Data/SqlClient/TdsParserStateObjectNative.cs | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 954164bd14..7095c8f51d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -182,12 +182,16 @@ internal override void CreatePhysicalSNIHandle( } else { - // This will signal to the interop layer that we need to retrieve the SPN + // Empty signifies to interop layer that SPN needs to be generated serverSPN = string.Empty; } } ConsumerInfo myInfo = CreateConsumerInfo(async); + + // serverName : serverInfo.ExtendedServerName + // may not use this serverName as key + SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index ec4c1782f3..2741fdf9f3 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -155,8 +155,10 @@ internal override void CreatePhysicalSNIHandle( { if (isIntegratedSecurity) { + // now allocate proper length of buffer if (!string.IsNullOrEmpty(serverSPN)) { + // Native SNI requires the Unicode encoding and any other encoding like UTF8 breaks the code. SqlClientEventSource.Log.TryTraceEvent(" Server SPN `{0}` from the connection string is used.", serverSPN); } else @@ -218,6 +220,7 @@ internal override void Dispose() SafeHandle packetHandle = _sniPacket; SafeHandle sessionHandle = _sessionHandle; SafeHandle asyncAttnPacket = _sniAsyncAttnPacket; + _sniPacket = null; _sessionHandle = null; _sniAsyncAttnPacket = null; @@ -226,11 +229,6 @@ internal override void Dispose() if (sessionHandle != null || packetHandle != null) { - // Comment CloseMARSSession - // UNDONE - if there are pending reads or writes on logical connections, we need to block - // here for the callbacks!!! This only applies to async. Should be fixed by async fixes for - // AD unload/exit. - if (packetHandle != null) { packetHandle.Dispose(); @@ -239,6 +237,7 @@ internal override void Dispose() { asyncAttnPacket.Dispose(); } + if (sessionHandle != null) { sessionHandle.Dispose(); From dadc19e4f15dfbf3ba12384552a3a3479ea1a29d Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:15:17 +0100 Subject: [PATCH 03/14] netcore: sync tracing --- .../src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 7095c8f51d..5622b9e95c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -178,7 +178,7 @@ internal override void CreatePhysicalSNIHandle( if (!string.IsNullOrEmpty(serverSPN)) { // Native SNI requires the Unicode encoding and any other encoding like UTF8 breaks the code. - SqlClientEventSource.Log.TryTraceEvent("<{0}.{1}|SEC> Server SPN `{2}` from the connection string is used.", nameof(TdsParserStateObjectNative), nameof(CreatePhysicalSNIHandle), serverSPN); + SqlClientEventSource.Log.TryTraceEvent(" Server SPN `{0}` from the connection string is used.", serverSPN); } else { @@ -269,6 +269,7 @@ protected override void FreeGcHandle(int remaining, bool release) { if ((0 == remaining || release) && _gcHandle.IsAllocated) { + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, FREEING HANDLE!", ObjectID); _gcHandle.Free(); } } From 4cacea7f172a13d5aaf05a4ca9533e12393b1dcd Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:17:25 +0100 Subject: [PATCH 04/14] netcore: remove unused methods ReadAsyncCallback and WriteAsyncCallback only recursed. Also sync whitespace in netfx. --- .../Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 4 ---- .../Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 5622b9e95c..0bd0d3930b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -213,10 +213,6 @@ protected override bool CheckPacket(PacketHandle packet, TaskCompletionSource ReadAsyncCallback(key, packet, error); - - public void WriteAsyncCallback(IntPtr key, IntPtr packet, uint sniError) => WriteAsyncCallback(key, packet, sniError); - protected override void RemovePacketFromPendingList(PacketHandle ptr) { Debug.Assert(ptr.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 2741fdf9f3..1363d8071a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -434,7 +434,8 @@ internal override SniErrorDetails GetErrorDetails() { SniNativeWrapper.SniGetLastError(out SniError sniError); - return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError, (int)sniError.provider, sniError.lineNumber, sniError.function); + return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError, + (int)sniError.provider, sniError.lineNumber, sniError.function); } internal override void DisposePacketCache() From c111e6b9566b64aabe486279d2f869d2c408896c Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:18:27 +0100 Subject: [PATCH 05/14] netfx: sync null coalescing operator usage --- .../Data/SqlClient/TdsParserStateObjectNative.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 1363d8071a..5b88a1a5b0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -229,14 +229,8 @@ internal override void Dispose() if (sessionHandle != null || packetHandle != null) { - if (packetHandle != null) - { - packetHandle.Dispose(); - } - if (asyncAttnPacket != null) - { - asyncAttnPacket.Dispose(); - } + packetHandle?.Dispose(); + asyncAttnPacket?.Dispose(); if (sessionHandle != null) { From 439eac148bb13a3993a13c1d925676baf3948252 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:22:46 +0100 Subject: [PATCH 06/14] netfx, netcore: cleanup SQLFallbackDNSCache call Also, netfx: sync of AuthProviderInfo population to match netcore. We were assigning default values. --- .../Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 3 +-- .../Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 0bd0d3930b..5decbc6863 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -192,8 +192,7 @@ internal override void CreatePhysicalSNIHandle( // serverName : serverInfo.ExtendedServerName // may not use this serverName as key - SQLDNSInfo cachedDNSInfo; - bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, flushCache, !async, fParallel, iPAddressPreference, cachedDNSInfo, hostNameInCertificate); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 5b88a1a5b0..78e2c766bc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -173,7 +173,7 @@ internal override void CreatePhysicalSNIHandle( // serverName : serverInfo.ExtendedServerName // may not use this serverName as key - _ = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); + SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, @@ -403,10 +403,6 @@ internal override uint EnableSsl(ref uint info, bool tlsFirst, string serverCert AuthProviderInfo authInfo = new AuthProviderInfo(); authInfo.flags = info; authInfo.tlsFirst = tlsFirst; - authInfo.certId = null; - authInfo.certHash = false; - authInfo.clientCertificateCallbackContext = IntPtr.Zero; - authInfo.clientCertificateCallback = null; authInfo.serverCertFileName = string.IsNullOrEmpty(serverCertificateFilename) ? null : serverCertificateFilename; // Add SSL (Encryption) SNI provider. From 3eddf97169a25f3ae0473b539281b75db28c7e35 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:26:00 +0100 Subject: [PATCH 07/14] netfx: remove unnecessary default values for CreatePhysicalSNIHandle parameters --- .../Data/SqlClient/TdsParserStateObjectNative.cs | 8 ++++---- .../src/Microsoft/Data/SqlClient/TdsParserStateObject.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 78e2c766bc..933ad62f48 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -148,10 +148,10 @@ internal override void CreatePhysicalSNIHandle( string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, string serverSPN, - bool isIntegratedSecurity = false, - bool tlsFirst = false, - string hostNameInCertificate = "", - string serverCertificateFilename = "") + bool isIntegratedSecurity, + bool tlsFirst, + string hostNameInCertificate, + string serverCertificateFilename) { if (isIntegratedSecurity) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index cac03827ab..64052b16d8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -527,10 +527,10 @@ internal abstract void CreatePhysicalSNIHandle( string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, string serverSPN, - bool isIntegratedSecurity = false, - bool tlsFirst = false, - string hostNameInCertificate = "", - string serverCertificateFilename = ""); + bool isIntegratedSecurity, + bool tlsFirst, + string hostNameInCertificate, + string serverCertificateFilename); internal abstract uint EnableSsl(ref uint info, bool tlsFirst, string serverCertificateFilename); From c30140ca51fa8e1f6cce351e784eeac08efdce59 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:37:18 +0100 Subject: [PATCH 08/14] netcore, netfx: sync SNIHandle construction Also sync debug assertion for SessionHandle --- .../Data/SqlClient/TdsParserStateObjectNative.cs | 8 +++++++- .../Data/SqlClient/TdsParserStateObjectNative.cs | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 5decbc6863..1cd0114bdf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -195,7 +195,11 @@ internal override void CreatePhysicalSNIHandle( SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, - flushCache, !async, fParallel, iPAddressPreference, cachedDNSInfo, hostNameInCertificate); + flushCache, !async, fParallel, +#if NETFRAMEWORK + transparentNetworkResolutionState, totalTimeout, +#endif + iPAddressPreference, cachedDNSInfo, hostNameInCertificate); resolvedSpn = new(serverSPN.TrimEnd()); } @@ -291,7 +295,9 @@ internal override uint CheckConnection() internal override PacketHandle ReadAsync(SessionHandle handle, out uint error) { +#if NET Debug.Assert(handle.Type == SessionHandle.NativeHandleType, "unexpected handle type when requiring NativePointer"); +#endif IntPtr readPacketPtr = IntPtr.Zero; error = SniNativeWrapper.SniReadAsync(handle.NativeHandle, ref readPacketPtr); return PacketHandle.FromNativePointer(readPacketPtr); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 933ad62f48..8d2ae63410 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -175,8 +175,11 @@ internal override void CreatePhysicalSNIHandle( SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, - out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, + _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, + flushCache, !async, fParallel, +#if NETFRAMEWORK + transparentNetworkResolutionState, totalTimeout, +#endif iPAddressPreference, cachedDNSInfo, hostNameInCertificate); resolvedSpn = new(serverSPN.TrimEnd()); } @@ -273,6 +276,9 @@ internal override uint CheckConnection() internal override PacketHandle ReadAsync(SessionHandle handle, out uint error) { +#if NET + Debug.Assert(handle.Type == SessionHandle.NativeHandleType, "unexpected handle type when requiring NativePointer"); +#endif IntPtr readPacketPtr = IntPtr.Zero; error = SniNativeWrapper.SniReadAsync(handle.NativeHandle, ref readPacketPtr); return PacketHandle.FromNativePointer(readPacketPtr); From b9e2beaf3fb35dcacae693ef694c955067ef70a0 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:43:18 +0100 Subject: [PATCH 09/14] netcore: remove hardcoded SSL protocol flags NativeProtocols enum values are aligned with the SslProtocol values. The output is only ever used to return the SSL warning, which checks whether any of each relevant SSL protocol's bits are set. Normalization of the client and server bits are irrelevant for this purpose - the SSL warning tests for both. --- .../SqlClient/TdsParserStateObjectNative.cs | 55 +------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 1cd0114bdf..b359ff93ca 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -17,25 +17,6 @@ namespace Microsoft.Data.SqlClient { internal class TdsParserStateObjectNative : TdsParserStateObject { - // protocol versions from native sni - [Flags] - private enum NativeProtocols - { - SP_PROT_SSL2_SERVER = 0x00000004, - SP_PROT_SSL2_CLIENT = 0x00000008, - SP_PROT_SSL3_SERVER = 0x00000010, - SP_PROT_SSL3_CLIENT = 0x00000020, - SP_PROT_TLS1_0_SERVER = 0x00000040, - SP_PROT_TLS1_0_CLIENT = 0x00000080, - SP_PROT_TLS1_1_SERVER = 0x00000100, - SP_PROT_TLS1_1_CLIENT = 0x00000200, - SP_PROT_TLS1_2_SERVER = 0x00000400, - SP_PROT_TLS1_2_CLIENT = 0x00000800, - SP_PROT_TLS1_3_SERVER = 0x00001000, - SP_PROT_TLS1_3_CLIENT = 0x00002000, - SP_PROT_NONE = 0x0 - } - private SNIHandle _sessionHandle = null; // the SNI handle we're to work on private SNIPacket _sniPacket = null; // Will have to re-vamp this for MARS @@ -440,42 +421,8 @@ internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) internal override uint WaitForSSLHandShakeToComplete(out SslProtocols protocolVersion) { uint returnValue = SniNativeWrapper.SniWaitForSslHandshakeToComplete(Handle, GetTimeoutRemaining(), out uint nativeProtocolVersion); - var nativeProtocol = (NativeProtocols)nativeProtocolVersion; -#pragma warning disable CA5398 // Avoid hardcoded SslProtocols values - if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_2_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_2_SERVER)) - { - protocolVersion = SslProtocols.Tls12; - } - else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_3_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_3_SERVER)) - { - /* The SslProtocols.Tls13 is supported by netcoreapp3.1 and later */ - protocolVersion = SslProtocols.Tls13; - } - else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_1_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_1_SERVER)) - { - protocolVersion = SslProtocols.Tls11; - } - else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_0_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_TLS1_0_SERVER)) - { - protocolVersion = SslProtocols.Tls; - } - else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL3_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL3_SERVER)) - { - // SSL 2.0 and 3.0 are only referenced to log a warning, not explicitly used for connections -#pragma warning disable CS0618, CA5397 - protocolVersion = SslProtocols.Ssl3; - } - else if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL2_CLIENT) || nativeProtocol.HasFlag(NativeProtocols.SP_PROT_SSL2_SERVER)) - { - protocolVersion = SslProtocols.Ssl2; -#pragma warning restore CS0618, CA5397 - } - else //if (nativeProtocol.HasFlag(NativeProtocols.SP_PROT_NONE)) - { - protocolVersion = SslProtocols.None; - } -#pragma warning restore CA5398 // Avoid hardcoded SslProtocols values + protocolVersion = (SslProtocols)nativeProtocolVersion; return returnValue; } From b4a962cbae2559b86c02f896546a271ac737af5f Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:44:47 +0100 Subject: [PATCH 10/14] netcore: sync timeouts of ReadSyncOverAsync This is only ever called with a timeoutRemaining parameter value of GetTimeoutRemaining(). --- .../src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index b359ff93ca..ccd38af9e6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -288,7 +288,7 @@ internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint { SNIHandle handle = Handle ?? throw ADP.ClosedConnectionError(); IntPtr readPacketPtr = IntPtr.Zero; - error = SniNativeWrapper.SniReadSyncOverAsync(handle, ref readPacketPtr, GetTimeoutRemaining()); + error = SniNativeWrapper.SniReadSyncOverAsync(handle, ref readPacketPtr, timeoutRemaining); return PacketHandle.FromNativePointer(readPacketPtr); } From d1d8fa7b7e4abf02a067f3fc0ee2d1ad334bbb69 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:51:36 +0100 Subject: [PATCH 11/14] netcore: sync server certificate filename marshalling Send a null value to the native SNI by default, not an empty string --- .../src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index ccd38af9e6..8d2ae63410 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -409,7 +409,7 @@ internal override uint EnableSsl(ref uint info, bool tlsFirst, string serverCert AuthProviderInfo authInfo = new AuthProviderInfo(); authInfo.flags = info; authInfo.tlsFirst = tlsFirst; - authInfo.serverCertFileName = serverCertificateFilename; + authInfo.serverCertFileName = string.IsNullOrEmpty(serverCertificateFilename) ? null : serverCertificateFilename; // Add SSL (Encryption) SNI provider. return SniNativeWrapper.SniAddProvider(Handle, Provider.SSL_PROV, ref authInfo); From 632b750fd9462d952bd14518c8291e07cb90247b Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:50:20 +0100 Subject: [PATCH 12/14] Merge TdsParserStateObjectNative --- .../src/Microsoft.Data.SqlClient.csproj | 5 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../SqlClient/TdsParserStateObjectNative.cs | 507 ------------------ .../TdsParserStateObjectNative.Windows.cs} | 0 4 files changed, 6 insertions(+), 510 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs rename src/Microsoft.Data.SqlClient/{netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs => src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs} (100%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 926ebf3580..e7c50f0640 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -973,12 +973,13 @@ Microsoft\Data\SqlClient\TdsParserStateObjectFactory.Windows.cs + + Microsoft\Data\SqlClient\TdsParserStateObjectNative.Windows.cs + Microsoft\Data\SqlTypes\SqlFileStream.Windows.cs - - ILLink.Substitutions.xml Resources\ILLink.Substitutions.Windows.xml diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 41a3fe3f68..cb7b81c5aa 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -924,6 +924,9 @@ Microsoft\Data\SqlClient\TdsParserStateObjectFactory.Windows.cs + + Microsoft\Data\SqlClient\TdsParserStateObjectNative.Windows.cs + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs @@ -996,7 +999,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs deleted file mode 100644 index 8d2ae63410..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ /dev/null @@ -1,507 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Net; -using System.Runtime.InteropServices; -using System.Security.Authentication; -using System.Threading.Tasks; -using Interop.Windows.Sni; -using Microsoft.Data.Common; -using Microsoft.Data.ProviderBase; - -namespace Microsoft.Data.SqlClient -{ - internal class TdsParserStateObjectNative : TdsParserStateObject - { - private SNIHandle _sessionHandle = null; // the SNI handle we're to work on - - private SNIPacket _sniPacket = null; // Will have to re-vamp this for MARS - internal SNIPacket _sniAsyncAttnPacket = null; // Packet to use to send Attn - private readonly WritePacketCache _writePacketCache = new WritePacketCache(); // Store write packets that are ready to be re-used - - private GCHandle _gcHandle; // keeps this object alive until we're closed. - - private readonly Dictionary _pendingWritePackets = new Dictionary(); // Stores write packets that have been sent to SNI, but have not yet finished writing (i.e. we are waiting for SNI's callback) - - internal TdsParserStateObjectNative(TdsParser parser, TdsParserStateObject physicalConnection, bool async) - : base(parser, physicalConnection, async) - { - } - - internal TdsParserStateObjectNative(TdsParser parser) - : base(parser) - { - } - - #region Properties - - internal SNIHandle Handle => _sessionHandle; - - internal override uint Status => _sessionHandle != null ? _sessionHandle.Status : TdsEnums.SNI_UNINITIALIZED; - - internal override SessionHandle SessionHandle => SessionHandle.FromNativeHandle(_sessionHandle); - - protected override PacketHandle EmptyReadPacket => PacketHandle.FromNativePointer(default); - - internal override Guid? SessionId => default; - - #endregion - - protected override void CreateSessionHandle(TdsParserStateObject physicalConnection, bool async) - { - Debug.Assert(physicalConnection is TdsParserStateObjectNative, "Expected a stateObject of type " + this.GetType()); - TdsParserStateObjectNative nativeSNIObject = physicalConnection as TdsParserStateObjectNative; - ConsumerInfo myInfo = CreateConsumerInfo(async); - - SQLDNSInfo cachedDNSInfo; - bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCache, out cachedDNSInfo); - - _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); - } - - // Retrieve the IP and port number from native SNI for TCP protocol. The IP information is stored temporarily in the - // pendingSQLDNSObject but not in the DNS Cache at this point. We only add items to the DNS Cache after we receive the - // IsSupported flag as true in the feature ext ack from server. - internal override void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo) - { - uint result; - ushort portFromSNI = 0; - string IPStringFromSNI = string.Empty; - IPAddress IPFromSNI; - _parser.isTcpProtocol = false; - Provider providerNumber = Provider.INVALID_PROV; - - if (string.IsNullOrEmpty(userProtocol)) - { - - result = SniNativeWrapper.SniGetProviderNumber(Handle, ref providerNumber); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetProviderNumber"); - _parser.isTcpProtocol = (providerNumber == Provider.TCP_PROV); - } - else if (userProtocol == TdsEnums.TCP) - { - _parser.isTcpProtocol = true; - } - - // serverInfo.UserProtocol could be empty - if (_parser.isTcpProtocol) - { - result = SniNativeWrapper.SniGetConnectionPort(Handle, ref portFromSNI); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionPort"); - - result = SniNativeWrapper.SniGetConnectionIpString(Handle, ref IPStringFromSNI); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionIPString"); - - pendingDNSInfo = new SQLDNSInfo(DNSCacheKey, null, null, portFromSNI.ToString()); - - if (IPAddress.TryParse(IPStringFromSNI, out IPFromSNI)) - { - if (System.Net.Sockets.AddressFamily.InterNetwork == IPFromSNI.AddressFamily) - { - pendingDNSInfo.AddrIPv4 = IPStringFromSNI; - } - else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == IPFromSNI.AddressFamily) - { - pendingDNSInfo.AddrIPv6 = IPStringFromSNI; - } - } - } - else - { - pendingDNSInfo = null; - } - } - - private ConsumerInfo CreateConsumerInfo(bool async) - { - ConsumerInfo myInfo = new ConsumerInfo(); - - Debug.Assert(_outBuff.Length == _inBuff.Length, "Unexpected unequal buffers."); - - myInfo.defaultBufferSize = _outBuff.Length; // Obtain packet size from outBuff size. - - if (async) - { - myInfo.readDelegate = SNILoadHandle.SingletonInstance.ReadAsyncCallbackDispatcher; - myInfo.writeDelegate = SNILoadHandle.SingletonInstance.WriteAsyncCallbackDispatcher; - _gcHandle = GCHandle.Alloc(this, GCHandleType.Normal); - myInfo.key = (IntPtr)_gcHandle; - } - return myInfo; - } - - internal override void CreatePhysicalSNIHandle( - string serverName, - TimeoutTimer timeout, - out byte[] instanceName, - out ManagedSni.ResolvedServerSpn resolvedSpn, - bool flushCache, - bool async, - bool fParallel, - TransparentNetworkResolutionState transparentNetworkResolutionState, - int totalTimeout, - SqlConnectionIPAddressPreference iPAddressPreference, - string cachedFQDN, - ref SQLDNSInfo pendingDNSInfo, - string serverSPN, - bool isIntegratedSecurity, - bool tlsFirst, - string hostNameInCertificate, - string serverCertificateFilename) - { - if (isIntegratedSecurity) - { - // now allocate proper length of buffer - if (!string.IsNullOrEmpty(serverSPN)) - { - // Native SNI requires the Unicode encoding and any other encoding like UTF8 breaks the code. - SqlClientEventSource.Log.TryTraceEvent(" Server SPN `{0}` from the connection string is used.", serverSPN); - } - else - { - // Empty signifies to interop layer that SPN needs to be generated - serverSPN = string.Empty; - } - } - - ConsumerInfo myInfo = CreateConsumerInfo(async); - - // serverName : serverInfo.ExtendedServerName - // may not use this serverName as key - - SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); - - _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, - flushCache, !async, fParallel, -#if NETFRAMEWORK - transparentNetworkResolutionState, totalTimeout, -#endif - iPAddressPreference, cachedDNSInfo, hostNameInCertificate); - resolvedSpn = new(serverSPN.TrimEnd()); - } - - protected override uint SniPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) - { - Debug.Assert(packet.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); - return SniNativeWrapper.SniPacketGetData(packet.NativePointer, _inBuff, ref dataSize); - } - - protected override bool CheckPacket(PacketHandle packet, TaskCompletionSource source) - { - Debug.Assert(packet.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); - IntPtr ptr = packet.NativePointer; - return IntPtr.Zero == ptr || IntPtr.Zero != ptr && source != null; - } - - protected override void RemovePacketFromPendingList(PacketHandle ptr) - { - Debug.Assert(ptr.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); - IntPtr pointer = ptr.NativePointer; - - lock (_writePacketLockObject) - { - if (_pendingWritePackets.TryGetValue(pointer, out SNIPacket recoveredPacket)) - { - _pendingWritePackets.Remove(pointer); - _writePacketCache.Add(recoveredPacket); - } -#if DEBUG - else - { - Debug.Fail("Removing a packet from the pending list that was never added to it"); - } -#endif - } - } - - internal override void Dispose() - { - SafeHandle packetHandle = _sniPacket; - SafeHandle sessionHandle = _sessionHandle; - SafeHandle asyncAttnPacket = _sniAsyncAttnPacket; - - _sniPacket = null; - _sessionHandle = null; - _sniAsyncAttnPacket = null; - - DisposeCounters(); - - if (sessionHandle != null || packetHandle != null) - { - packetHandle?.Dispose(); - asyncAttnPacket?.Dispose(); - - if (sessionHandle != null) - { - sessionHandle.Dispose(); - DecrementPendingCallbacks(true); // Will dispose of GC handle. - } - } - - DisposePacketCache(); - } - - protected override void FreeGcHandle(int remaining, bool release) - { - if ((0 == remaining || release) && _gcHandle.IsAllocated) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, FREEING HANDLE!", ObjectID); - _gcHandle.Free(); - } - } - - internal override bool IsFailedHandle() => _sessionHandle.Status != TdsEnums.SNI_SUCCESS; - - internal override bool IsPacketEmpty(PacketHandle readPacket) - { - Debug.Assert(readPacket.Type == PacketHandle.NativePointerType || readPacket.Type == 0, "unexpected packet type when requiring NativePointer"); - return IntPtr.Zero == readPacket.NativePointer; - } - - internal override void ReleasePacket(PacketHandle syncReadPacket) - { - Debug.Assert(syncReadPacket.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); - SniNativeWrapper.SniPacketRelease(syncReadPacket.NativePointer); - } - - internal override uint CheckConnection() - { - SNIHandle handle = Handle; - return handle == null ? TdsEnums.SNI_SUCCESS : SniNativeWrapper.SniCheckConnection(handle); - } - - internal override PacketHandle ReadAsync(SessionHandle handle, out uint error) - { -#if NET - Debug.Assert(handle.Type == SessionHandle.NativeHandleType, "unexpected handle type when requiring NativePointer"); -#endif - IntPtr readPacketPtr = IntPtr.Zero; - error = SniNativeWrapper.SniReadAsync(handle.NativeHandle, ref readPacketPtr); - return PacketHandle.FromNativePointer(readPacketPtr); - } - - internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint error) - { - SNIHandle handle = Handle ?? throw ADP.ClosedConnectionError(); - IntPtr readPacketPtr = IntPtr.Zero; - error = SniNativeWrapper.SniReadSyncOverAsync(handle, ref readPacketPtr, timeoutRemaining); - return PacketHandle.FromNativePointer(readPacketPtr); - } - - internal override PacketHandle CreateAndSetAttentionPacket() - { - SNIPacket attnPacket = new SNIPacket(Handle); - _sniAsyncAttnPacket = attnPacket; - SniNativeWrapper.SniPacketSetData(attnPacket, SQL.AttentionHeader, TdsEnums.HEADER_LEN); - return PacketHandle.FromNativePacket(attnPacket); - } - - internal override uint WritePacket(PacketHandle packet, bool sync) - { - Debug.Assert(packet.Type == PacketHandle.NativePacketType, "unexpected packet type when requiring NativePacket"); - return SniNativeWrapper.SniWritePacket(Handle, packet.NativePacket, sync); - } - - internal override PacketHandle AddPacketToPendingList(PacketHandle packetToAdd) - { - Debug.Assert(packetToAdd.Type == PacketHandle.NativePacketType, "unexpected packet type when requiring NativePacket"); - SNIPacket packet = packetToAdd.NativePacket; - Debug.Assert(packet == _sniPacket, "Adding a packet other than the current packet to the pending list"); - _sniPacket = null; - IntPtr pointer = packet.DangerousGetHandle(); - - lock (_writePacketLockObject) - { - _pendingWritePackets.Add(pointer, packet); - } - - return PacketHandle.FromNativePointer(pointer); - } - - internal override bool IsValidPacket(PacketHandle packetPointer) - { - Debug.Assert(packetPointer.Type == PacketHandle.NativePointerType || packetPointer.Type == PacketHandle.NativePacketType, "unexpected packet type when requiring NativePointer"); - - return (packetPointer.Type == PacketHandle.NativePointerType && packetPointer.NativePointer != IntPtr.Zero) - || (packetPointer.Type == PacketHandle.NativePacketType && packetPointer.NativePacket != null); - } - - internal override PacketHandle GetResetWritePacket(int dataSize) - { - if (_sniPacket != null) - { - SniNativeWrapper.SniPacketReset(Handle, IoType.WRITE, _sniPacket, ConsumerNumber.SNI_Consumer_SNI); - } - else - { - lock (_writePacketLockObject) - { - _sniPacket = _writePacketCache.Take(Handle); - } - } - return PacketHandle.FromNativePacket(_sniPacket); - } - - internal override void ClearAllWritePackets() - { - if (_sniPacket != null) - { - _sniPacket.Dispose(); - _sniPacket = null; - } - lock (_writePacketLockObject) - { - Debug.Assert(_pendingWritePackets.Count == 0 && _asyncWriteCount == 0, "Should not clear all write packets if there are packets pending"); - _writePacketCache.Clear(); - } - } - - internal override void SetPacketData(PacketHandle packet, byte[] buffer, int bytesUsed) - { - Debug.Assert(packet.Type == PacketHandle.NativePacketType, "unexpected packet type when requiring NativePacket"); - SniNativeWrapper.SniPacketSetData(packet.NativePacket, buffer, bytesUsed); - } - - internal override uint SniGetConnectionId(ref Guid clientConnectionId) - => SniNativeWrapper.SniGetConnectionId(Handle, ref clientConnectionId); - - internal override uint DisableSsl() - => SniNativeWrapper.SniRemoveProvider(Handle, Provider.SSL_PROV); - - internal override uint EnableMars(ref uint info) - => SniNativeWrapper.SniAddProvider(Handle, Provider.SMUX_PROV, ref info); - - internal override uint PostReadAsyncForMars(TdsParserStateObject physicalStateObject) - { - // HACK HACK HACK - for Async only - // Have to post read to initialize MARS - will get callback on this when connection goes - // down or is closed. - - PacketHandle temp = default; - uint error = TdsEnums.SNI_SUCCESS; - - IncrementPendingCallbacks(); - SessionHandle handle = SessionHandle; - // we do not need to consider partial packets when making this read because we - // expect this read to pend. a partial packet should not exist at setup of the - // parser - Debug.Assert(physicalStateObject.PartialPacket == null); - temp = ReadAsync(handle, out error); - - Debug.Assert(temp.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer"); - - if (temp.NativePointer != IntPtr.Zero) - { - // Be sure to release packet, otherwise it will be leaked by native. - ReleasePacket(temp); - } - - Debug.Assert(IntPtr.Zero == temp.NativePointer, "unexpected syncReadPacket without corresponding SNIPacketRelease"); - return error; - } - - internal override uint EnableSsl(ref uint info, bool tlsFirst, string serverCertificateFilename) - { - AuthProviderInfo authInfo = new AuthProviderInfo(); - authInfo.flags = info; - authInfo.tlsFirst = tlsFirst; - authInfo.serverCertFileName = string.IsNullOrEmpty(serverCertificateFilename) ? null : serverCertificateFilename; - - // Add SSL (Encryption) SNI provider. - return SniNativeWrapper.SniAddProvider(Handle, Provider.SSL_PROV, ref authInfo); - } - - internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) - => SniNativeWrapper.SniSetInfo(Handle, QueryType.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize); - - internal override uint WaitForSSLHandShakeToComplete(out SslProtocols protocolVersion) - { - uint returnValue = SniNativeWrapper.SniWaitForSslHandshakeToComplete(Handle, GetTimeoutRemaining(), out uint nativeProtocolVersion); - - protocolVersion = (SslProtocols)nativeProtocolVersion; - return returnValue; - } - - internal override SniErrorDetails GetErrorDetails() - { - SniNativeWrapper.SniGetLastError(out SniError sniError); - - return new SniErrorDetails(sniError.errorMessage, sniError.nativeError, sniError.sniError, - (int)sniError.provider, sniError.lineNumber, sniError.function); - } - - internal override void DisposePacketCache() - { - lock (_writePacketLockObject) - { - _writePacketCache.Dispose(); - // Do not set _writePacketCache to null, just in case a WriteAsyncCallback completes after this point - } - } - - internal override SspiContextProvider CreateSspiContextProvider() => new NativeSspiContextProvider(); - - private sealed class WritePacketCache : IDisposable - { - private bool _disposed; - private Stack _packets; - - public WritePacketCache() - { - _disposed = false; - _packets = new Stack(); - } - - public SNIPacket Take(SNIHandle sniHandle) - { - SNIPacket packet; - if (_packets.Count > 0) - { - // Success - reset the packet - packet = _packets.Pop(); - SniNativeWrapper.SniPacketReset(sniHandle, IoType.WRITE, packet, ConsumerNumber.SNI_Consumer_SNI); - } - else - { - // Failed to take a packet - create a new one - packet = new SNIPacket(sniHandle); - } - return packet; - } - - public void Add(SNIPacket packet) - { - if (!_disposed) - { - _packets.Push(packet); - } - else - { - // If we're disposed, then get rid of any packets added to us - packet.Dispose(); - } - } - - public void Clear() - { - while (_packets.Count > 0) - { - _packets.Pop().Dispose(); - } - } - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - Clear(); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs From 1e195f6c7c3d60f8b2258a56dbdfc827c5d1985b Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:56:08 +0100 Subject: [PATCH 13/14] First code review response Maintained comment in Dispose method post-merge --- .../Data/SqlClient/TdsParserStateObjectNative.Windows.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs index 8d2ae63410..3fc11f7285 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs @@ -232,6 +232,11 @@ internal override void Dispose() if (sessionHandle != null || packetHandle != null) { + // Comment CloseMARSSession + // UNDONE - if there are pending reads or writes on logical connections, we need to block + // here for the callbacks!!! This only applies to async. Should be fixed by async fixes for + // AD unload/exit. + packetHandle?.Dispose(); asyncAttnPacket?.Dispose(); From 2a3c83f983f23306794cb2e54c9cfcf63eddf408 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:21:07 +0100 Subject: [PATCH 14/14] Add explicit handling of SNI-layer SSL protocols This guards against future changes to the SslProtocols enum --- .../src/Microsoft.Data.SqlClient.csproj | 3 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + .../Interop/Windows/Sni/ISniNativeMethods.cs | 2 +- .../Windows/Sni/SniNativeMethods.netcore.cs | 4 +- .../Sni/SniNativeMethodsArm64.netfx.cs | 4 +- .../Sni/SniNativeMethodsNotSupported.netfx.cs | 2 +- .../Windows/Sni/SniNativeMethodsX64.netfx.cs | 4 +- .../Windows/Sni/SniNativeMethodsX86.netfx.cs | 4 +- .../Interop/Windows/Sni/SniNativeWrapper.cs | 56 +++++++++++++++++-- .../Interop/Windows/Sni/SniSslProtocols.cs | 32 +++++++++++ .../TdsParserStateObjectNative.Windows.cs | 9 +-- 11 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniSslProtocols.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index e7c50f0640..c9de00df6d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -928,6 +928,9 @@ Interop\Windows\Sni\SniNativeWrapper.cs + + Interop\Windows\Sni\SniSslProtocols.cs + Interop\Windows\Sni\TransparentNetworkResolutionMode.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index cb7b81c5aa..de1e9b7c1f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -219,6 +219,9 @@ Interop\Windows\Sni\SqlDependencyProcessDispatcherStorage.netfx.cs + + Interop\Windows\Sni\SniSslProtocols.cs + Interop\Windows\Sni\TransparentNetworkResolutionMode.cs diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/ISniNativeMethods.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/ISniNativeMethods.cs index 109b7c27ae..c05c76b188 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/ISniNativeMethods.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/ISniNativeMethods.cs @@ -92,7 +92,7 @@ unsafe uint SniSecGenClientContextWrapper( uint SniTerminate(); - uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion); + uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion); uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethods.netcore.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethods.netcore.cs index a47af81e9f..b780f8f63c 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethods.netcore.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethods.netcore.cs @@ -148,7 +148,7 @@ public uint SniSetInfo(SNIHandle pConn, QueryType queryType, ref uint pbQueryInf public uint SniTerminate() => SNITerminate(); - public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion) => + public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion) => SNIWaitForSSLHandshakeToCompleteWrapper(pConn, dwMilliseconds, out pProtocolVersion); public uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket) => @@ -299,7 +299,7 @@ private static extern int SNIServerEnumReadWrapper( private static extern uint SNIWaitForSSLHandshakeToCompleteWrapper( [In] SNIHandle pConn, int dwMilliseconds, - out uint pProtocolVersion); + out SniSslProtocols pProtocolVersion); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteAsyncWrapper(SNIHandle pConn, [In] SNIPacket pPacket); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsArm64.netfx.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsArm64.netfx.cs index 1c60f92443..7839a047cd 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsArm64.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsArm64.netfx.cs @@ -148,7 +148,7 @@ public uint SniSetInfo(SNIHandle pConn, QueryType queryType, ref uint pbQueryInf public uint SniTerminate() => SNITerminate(); - public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion) => + public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion) => SNIWaitForSSLHandshakeToCompleteWrapper(pConn, dwMilliseconds, out pProtocolVersion); public uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket) => @@ -299,7 +299,7 @@ private static extern int SNIServerEnumReadWrapper( private static extern uint SNIWaitForSSLHandshakeToCompleteWrapper( [In] SNIHandle pConn, int dwMilliseconds, - out uint pProtocolVersion); + out SniSslProtocols pProtocolVersion); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteAsyncWrapper(SNIHandle pConn, [In] SNIPacket pPacket); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsNotSupported.netfx.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsNotSupported.netfx.cs index ba26fb0dc9..7139ee0b08 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsNotSupported.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsNotSupported.netfx.cs @@ -142,7 +142,7 @@ public uint SniSetInfo(SNIHandle pConn, QueryType queryType, ref uint pbQueryInf public uint SniTerminate() => throw ADP.SNIPlatformNotSupported(_architecture); - public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion) => + public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion) => throw ADP.SNIPlatformNotSupported(_architecture); public uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket) => diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX64.netfx.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX64.netfx.cs index 1c6519327f..e899250786 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX64.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX64.netfx.cs @@ -148,7 +148,7 @@ public uint SniSetInfo(SNIHandle pConn, QueryType queryType, ref uint pbQueryInf public uint SniTerminate() => SNITerminate(); - public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion) => + public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion) => SNIWaitForSSLHandshakeToCompleteWrapper(pConn, dwMilliseconds, out pProtocolVersion); public uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket) => @@ -299,7 +299,7 @@ private static extern int SNIServerEnumReadWrapper( private static extern uint SNIWaitForSSLHandshakeToCompleteWrapper( [In] SNIHandle pConn, int dwMilliseconds, - out uint pProtocolVersion); + out SniSslProtocols pProtocolVersion); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteAsyncWrapper(SNIHandle pConn, [In] SNIPacket pPacket); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX86.netfx.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX86.netfx.cs index fd2ab4644b..4c022821dd 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX86.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeMethodsX86.netfx.cs @@ -148,7 +148,7 @@ public uint SniSetInfo(SNIHandle pConn, QueryType queryType, ref uint pbQueryInf public uint SniTerminate() => SNITerminate(); - public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out uint pProtocolVersion) => + public uint SniWaitForSslHandshakeToComplete(SNIHandle pConn, int dwMilliseconds, out SniSslProtocols pProtocolVersion) => SNIWaitForSSLHandshakeToCompleteWrapper(pConn, dwMilliseconds, out pProtocolVersion); public uint SniWriteAsyncWrapper(SNIHandle pConn, SNIPacket pPacket) => @@ -299,7 +299,7 @@ private static extern int SNIServerEnumReadWrapper( private static extern uint SNIWaitForSSLHandshakeToCompleteWrapper( [In] SNIHandle pConn, int dwMilliseconds, - out uint pProtocolVersion); + out SniSslProtocols pProtocolVersion); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteAsyncWrapper(SNIHandle pConn, [In] SNIPacket pPacket); diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs index 6dc01dc31e..60f5f98dcd 100644 --- a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniNativeWrapper.cs @@ -376,19 +376,67 @@ internal static uint SniSetInfo(SNIHandle pConn, QueryType qType, ref uint pbQIn internal static uint SniTerminate() => s_nativeMethods.SniTerminate(); - + internal static uint SniWaitForSslHandshakeToComplete( SNIHandle pConn, int dwMilliseconds, - out uint pProtocolVersion) => - s_nativeMethods.SniWaitForSslHandshakeToComplete(pConn, dwMilliseconds, out pProtocolVersion); + out System.Security.Authentication.SslProtocols pProtocolVersion) + { + uint returnValue = s_nativeMethods.SniWaitForSslHandshakeToComplete(pConn, dwMilliseconds, out SniSslProtocols nativeProtocolVersion); + +#pragma warning disable CA5398 // Avoid hardcoded SslProtocols values + if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_TLS1_2) != 0) + { + pProtocolVersion = System.Security.Authentication.SslProtocols.Tls12; + } + else if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_TLS1_3) != 0) + { +#if NET + pProtocolVersion = System.Security.Authentication.SslProtocols.Tls13; +#else + // Only .NET Core supports SslProtocols.Tls13 + pProtocolVersion = (System.Security.Authentication.SslProtocols)0x3000; +#endif + } + else if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_TLS1_1) != 0) + { +#if NET8_0_OR_GREATER +#pragma warning disable SYSLIB0039 // Type or member is obsolete: TLS 1.0 & 1.1 are deprecated +#endif + pProtocolVersion = System.Security.Authentication.SslProtocols.Tls11; + } + else if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_TLS1_0) != 0) + { + pProtocolVersion = System.Security.Authentication.SslProtocols.Tls; +#if NET8_0_OR_GREATER +#pragma warning restore SYSLIB0039 // Type or member is obsolete: SSL and TLS 1.0 & 1.1 is deprecated +#endif + } + else if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_SSL3) != 0) + { + // SSL 2.0 and 3.0 are only referenced to log a warning, not explicitly used for connections +#pragma warning disable CS0618, CA5397 + pProtocolVersion = System.Security.Authentication.SslProtocols.Ssl3; + } + else if ((nativeProtocolVersion & SniSslProtocols.SP_PROT_SSL2) != 0) + { + pProtocolVersion = System.Security.Authentication.SslProtocols.Ssl2; +#pragma warning restore CS0618, CA5397 + } + else + { + pProtocolVersion = System.Security.Authentication.SslProtocols.None; + } +#pragma warning restore CA5398 // Avoid hardcoded SslProtocols values + return returnValue; + } internal static uint SniWritePacket(SNIHandle pConn, SNIPacket packet, bool sync) => sync ? s_nativeMethods.SniWriteSyncOverAsync(pConn, packet) : s_nativeMethods.SniWriteAsyncWrapper(pConn, packet); - #endregion +#endregion #region Private Methods diff --git a/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniSslProtocols.cs b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniSslProtocols.cs new file mode 100644 index 0000000000..cd9bf47ed3 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SniSslProtocols.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Interop.Windows.Sni +{ + internal enum SniSslProtocols : uint + { + // Protocol versions from native SNI + SP_PROT_SSL2_SERVER = 0x00000004, + SP_PROT_SSL2_CLIENT = 0x00000008, + SP_PROT_SSL3_SERVER = 0x00000010, + SP_PROT_SSL3_CLIENT = 0x00000020, + SP_PROT_TLS1_0_SERVER = 0x00000040, + SP_PROT_TLS1_0_CLIENT = 0x00000080, + SP_PROT_TLS1_1_SERVER = 0x00000100, + SP_PROT_TLS1_1_CLIENT = 0x00000200, + SP_PROT_TLS1_2_SERVER = 0x00000400, + SP_PROT_TLS1_2_CLIENT = 0x00000800, + SP_PROT_TLS1_3_SERVER = 0x00001000, + SP_PROT_TLS1_3_CLIENT = 0x00002000, + SP_PROT_NONE = 0x0, + + // Combinations for easier use when mapping to SslProtocols + SP_PROT_SSL2 = SP_PROT_SSL2_SERVER | SP_PROT_SSL2_CLIENT, + SP_PROT_SSL3 = SP_PROT_SSL3_SERVER | SP_PROT_SSL3_CLIENT, + SP_PROT_TLS1_0 = SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_0_CLIENT, + SP_PROT_TLS1_1 = SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_1_CLIENT, + SP_PROT_TLS1_2 = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_2_CLIENT, + SP_PROT_TLS1_3 = SP_PROT_TLS1_3_SERVER | SP_PROT_TLS1_3_CLIENT, + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs index 3fc11f7285..4efd94e112 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.Windows.cs @@ -423,13 +423,8 @@ internal override uint EnableSsl(ref uint info, bool tlsFirst, string serverCert internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) => SniNativeWrapper.SniSetInfo(Handle, QueryType.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize); - internal override uint WaitForSSLHandShakeToComplete(out SslProtocols protocolVersion) - { - uint returnValue = SniNativeWrapper.SniWaitForSslHandshakeToComplete(Handle, GetTimeoutRemaining(), out uint nativeProtocolVersion); - - protocolVersion = (SslProtocols)nativeProtocolVersion; - return returnValue; - } + internal override uint WaitForSSLHandShakeToComplete(out SslProtocols protocolVersion) => + SniNativeWrapper.SniWaitForSslHandshakeToComplete(Handle, GetTimeoutRemaining(), out protocolVersion); internal override SniErrorDetails GetErrorDetails() {