diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index e02711fe8d..a0cf118574 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -395,7 +395,6 @@ internal void Connect(ServerInfo serverInfo, } else { - _serverSpn = null; SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | Connection Object Id {0}, Authentication Mode: {1}", _connHandler.ObjectID, authType == SqlAuthenticationMethod.NotSpecified ? SqlAuthenticationMethod.SqlPassword.ToString() : authType.ToString()); } @@ -450,6 +449,8 @@ internal void Connect(ServerInfo serverInfo, false, true, fParallel, + TransparentNetworkResolutionState.DisabledMode, + -1, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject, @@ -550,6 +551,8 @@ internal void Connect(ServerInfo serverInfo, true, true, fParallel, + TransparentNetworkResolutionState.DisabledMode, + -1, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs index bb3b07b0e8..7ad67b522b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs @@ -31,7 +31,7 @@ internal static void PrepareConstrainedRegions() // Constructors // ////////////////// - internal TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async) + protected TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async) { // Construct a MARS session Debug.Assert(parser != null, "no parser?"); @@ -67,42 +67,19 @@ internal TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalCon // General methods // ///////////////////// - internal abstract void CreatePhysicalSNIHandle( - string serverName, - TimeoutTimer timeout, - out byte[] instanceName, - ref string[] spns, - bool flushCache, - bool async, - bool fParallel, - SqlConnectionIPAddressPreference iPAddressPreference, - string cachedFQDN, - ref SQLDNSInfo pendingDNSInfo, - string serverSPN, - bool isIntegratedSecurity = false, - bool tlsFirst = false, - string hostNameInCertificate = "", - string serverCertificateFilename = ""); - - internal abstract void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo); - - protected abstract void CreateSessionHandle(TdsParserStateObject physicalConnection, bool async); - - protected abstract void FreeGcHandle(int remaining, bool release); - internal abstract uint EnableSsl(ref uint info, bool tlsFirst, string serverCertificateFilename); internal abstract uint WaitForSSLHandShakeToComplete(out int protocolVersion); - internal abstract void Dispose(); - internal abstract uint CheckConnection(); internal int DecrementPendingCallbacks(bool release) { int remaining = Interlocked.Decrement(ref _pendingCallbacks); SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.DecrementPendingCallbacks | ADV | State Object Id {0}, after decrementing _pendingCallbacks: {1}", _objectID, _pendingCallbacks); + FreeGcHandle(remaining, release); + // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it Debug.Assert((remaining == -1 && SessionHandle.IsNull) || (0 <= remaining && remaining < 3), $"_pendingCallbacks values is invalid after decrementing: {remaining}"); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index a403f8b556..743f403b71 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -84,6 +84,8 @@ internal override void CreatePhysicalSNIHandle( bool flushCache, bool async, bool parallel, + TransparentNetworkResolutionState transparentNetworkResolutionState, + int totalTimeout, SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, 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 10e9d96eca..312c1eb368 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 @@ -52,14 +52,12 @@ internal TdsParserStateObjectNative(TdsParser parser, TdsParserStateObject physi { } - public TdsParserStateObjectNative(TdsParser parser) + internal TdsParserStateObjectNative(TdsParser parser) : base(parser) { } - //////////////// - // Properties // - //////////////// + #region Properties internal SNIHandle Handle => _sessionHandle; @@ -71,6 +69,8 @@ public TdsParserStateObjectNative(TdsParser parser) 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()); @@ -83,6 +83,9 @@ protected override void CreateSessionHandle(TdsParserStateObject physicalConnect _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; @@ -159,7 +162,9 @@ internal override void CreatePhysicalSNIHandle( bool flushCache, bool async, bool fParallel, - SqlConnectionIPAddressPreference ipPreference, + TransparentNetworkResolutionState transparentNetworkResolutionState, + int totalTimeout, + SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, string serverSPN, @@ -188,7 +193,7 @@ internal override void CreatePhysicalSNIHandle( bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, ref serverSPN, timeout.MillisecondsRemainingInt, out instanceName, - flushCache, !async, fParallel, ipPreference, cachedDNSInfo, hostNameInCertificate); + flushCache, !async, fParallel, iPAddressPreference, cachedDNSInfo, hostNameInCertificate); spns = new[] { serverSPN.TrimEnd() }; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 0715bd8205..33f4cedf9a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -121,7 +121,7 @@ internal sealed partial class TdsParser private bool _is2022 = false; - private string _serverSpn = null; + private string[] _serverSpn = null; // SqlStatistics private SqlStatistics _statistics = null; @@ -395,6 +395,11 @@ internal void Connect(ServerInfo serverInfo, ThrowExceptionAndWarning(_physicalStateObj); Debug.Fail("SNI returned status != success, but no error thrown?"); } + else + { + SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | Connection Object Id {0}, Authentication Mode: {1}", _connHandler.ObjectID, + authType == SqlAuthenticationMethod.NotSpecified ? SqlAuthenticationMethod.SqlPassword.ToString() : authType.ToString()); + } //Create LocalDB instance if necessary if (connHandler.ConnectionOptions.LocalDBInstance != null) @@ -408,28 +413,17 @@ internal void Connect(ServerInfo serverInfo, } } + _serverSpn = null; + // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { _authenticationProvider = _physicalStateObj.CreateSspiContextProvider(); - - if (!string.IsNullOrEmpty(serverInfo.ServerSPN)) - { - _serverSpn = serverInfo.ServerSPN; - SqlClientEventSource.Log.TryTraceEvent(" Server SPN `{0}` from the connection string is used.", serverInfo.ServerSPN); - } - else - { - // Empty signifies to interop layer that SPN needs to be generated - _serverSpn = string.Empty; - } - SqlClientEventSource.Log.TryTraceEvent(" SSPI or Active Directory Authentication Library for SQL Server based integrated authentication"); } else { _authenticationProvider = null; - _serverSpn = null; switch (authType) { @@ -516,7 +510,12 @@ internal void Connect(ServerInfo serverInfo, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, - hostNameInCertificate); + ref _connHandler.pendingSQLDNSObject, + serverInfo.ServerSPN, + integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated, + isTlsFirst, + hostNameInCertificate, + serverCertificateFilename); _authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this); @@ -559,7 +558,7 @@ internal void Connect(ServerInfo serverInfo, Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); // for DNS Caching phase 1 - AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); if (!ClientOSEncryptionSupport) { @@ -609,8 +608,13 @@ internal void Connect(ServerInfo serverInfo, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, - serverInfo.ResolvedServerName, - hostNameInCertificate); + FQDNforDNSCache, + ref _connHandler.pendingSQLDNSObject, + serverInfo.ServerSPN, + integratedSecurity, + isTlsFirst, + hostNameInCertificate, + serverCertificateFilename); _authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this); @@ -626,7 +630,7 @@ internal void Connect(ServerInfo serverInfo, SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); // for DNS Caching phase 1 - AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, serverCertificateFilename); status = ConsumePreLoginHandshake( diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs index 6841904b33..fca2878575 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs @@ -43,61 +43,7 @@ internal void BestEffortCleanup() } } } - - // 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 void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey) - { - uint result; - ushort portFromSNI = 0; - string IPStringFromSNI = string.Empty; - IPAddress IPFromSNI; - isTcpProtocol = false; - Provider providerNumber = Provider.INVALID_PROV; - - if (string.IsNullOrEmpty(userProtocol)) - { - - result = SniNativeWrapper.SniGetProviderNumber(_physicalStateObj.Handle, ref providerNumber); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetProviderNumber"); - isTcpProtocol = (providerNumber == Provider.TCP_PROV); - } - else if (userProtocol == TdsEnums.TCP) - { - isTcpProtocol = true; - } - - // serverInfo.UserProtocol could be empty - if (isTcpProtocol) - { - result = SniNativeWrapper.SniGetConnectionPort(_physicalStateObj.Handle, ref portFromSNI); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionPort"); - - - result = SniNativeWrapper.SniGetConnectionIpString(_physicalStateObj.Handle, ref IPStringFromSNI); - Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionIPString"); - - _connHandler.pendingSQLDNSObject = new SQLDNSInfo(DNSCacheKey, null, null, portFromSNI.ToString()); - - if (IPAddress.TryParse(IPStringFromSNI, out IPFromSNI)) - { - if (System.Net.Sockets.AddressFamily.InterNetwork == IPFromSNI.AddressFamily) - { - _connHandler.pendingSQLDNSObject.AddrIPv4 = IPStringFromSNI; - } - else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == IPFromSNI.AddressFamily) - { - _connHandler.pendingSQLDNSObject.AddrIPv6 = IPStringFromSNI; - } - } - } - else - { - _connHandler.pendingSQLDNSObject = null; - } - } - + // @TODO: Consider adopting this pattern for all usages of Run and rename to Run. internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs index cf54e0340f..1e645ceadd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs @@ -26,9 +26,6 @@ internal partial class TdsParserStateObject protected SNIPacket _sniPacket = null; // Will have to re-vamp this for MARS internal SNIPacket _sniAsyncAttnPacket = null; // Packet to use to send Attn - // Async variables - private GCHandle _gcHandle; // keeps this object alive until we're closed. - // Used for blanking out password in trace. internal int _tracePasswordOffset = 0; internal int _tracePasswordLength = 0; @@ -39,7 +36,7 @@ internal partial class TdsParserStateObject // Constructors // ////////////////// - protected TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, bool async) + protected TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalConnection, bool async) { // Construct a MARS session Debug.Assert(parser != null, "no parser?"); @@ -56,12 +53,8 @@ protected TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, b // Determine packet size based on physical connection buffer lengths. SetPacketSize(_parser._physicalStateObj._outBuff.Length); - ConsumerInfo myInfo = CreateConsumerInfo(async); - SQLDNSInfo cachedDNSInfo; - - SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCache, out cachedDNSInfo); + CreateSessionHandle(physicalConnection, async); - _sessionHandle = new SNIHandle(myInfo, physicalConnection, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); if (IsFailedHandle()) { AddError(parser.ProcessSNIError(this)); @@ -90,50 +83,6 @@ internal SNIHandle Handle // General methods // ///////////////////// - 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 void CreatePhysicalSNIHandle( - string serverName, - TimeoutTimer timeout, - out byte[] instanceName, - ref string spn, - bool flushCache, - bool async, - bool fParallel, - TransparentNetworkResolutionState transparentNetworkResolutionState, - int totalTimeout, - SqlConnectionIPAddressPreference ipPreference, - string cachedFQDN, - string hostNameInCertificate = "") - { - 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 spn, timeout.MillisecondsRemainingInt, - out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, - ipPreference, cachedDNSInfo, hostNameInCertificate); - } - internal uint CheckConnection() => SniNativeWrapper.SniCheckConnection(Handle); [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] @@ -142,11 +91,7 @@ internal int DecrementPendingCallbacks(bool release) int remaining = Interlocked.Decrement(ref _pendingCallbacks); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, after decrementing _pendingCallbacks: {1}", ObjectID, _pendingCallbacks); - if ((0 == remaining || release) && _gcHandle.IsAllocated) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, FREEING HANDLE!", ObjectID); - _gcHandle.Free(); - } + FreeGcHandle(remaining, release); // NOTE: TdsParserSessionPool may call DecrementPendingCallbacks on a TdsParserStateObject which is already disposed // This is not dangerous (since the stateObj is no longer in use), but we need to add a workaround in the assert for it @@ -154,50 +99,6 @@ internal int DecrementPendingCallbacks(bool release) return remaining; } - internal void Dispose() - { - - SafeHandle packetHandle = _sniPacket; - SafeHandle sessionHandle = _sessionHandle; - SafeHandle asyncAttnPacket = _sniAsyncAttnPacket; - _sniPacket = null; - _sessionHandle = null; - _sniAsyncAttnPacket = null; - - DisposeCounters(); - - 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. - - // TODO: Make this a BID trace point! - RuntimeHelpers.PrepareConstrainedRegions(); - try - { } - finally - { - if (packetHandle != null) - { - packetHandle.Dispose(); - } - if (asyncAttnPacket != null) - { - asyncAttnPacket.Dispose(); - } - if (sessionHandle != null) - { - sessionHandle.Dispose(); - DecrementPendingCallbacks(true); // Will dispose of GC handle. - } - } - } - - DisposePacketCache(); - } - /// /// Checks to see if the underlying connection is still valid (used by idle connection resiliency - for active connections) /// NOTE: This is not safe to do on a connection that is currently in use 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 2ad67cf2f4..005d2c5763 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 @@ -5,10 +5,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Net; using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Interop.Windows.Sni; using Microsoft.Data.Common; +using Microsoft.Data.ProviderBase; namespace Microsoft.Data.SqlClient { @@ -16,21 +20,21 @@ internal class TdsParserStateObjectNative : TdsParserStateObject { 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.Handle, async) + : base(parser, physicalConnection, async) { } - public TdsParserStateObjectNative(TdsParser parser) + internal TdsParserStateObjectNative(TdsParser parser) : base(parser) { } - //////////////// - // Properties // - //////////////// + #region Properties internal override uint Status => _sessionHandle != null ? _sessionHandle.Status : TdsEnums.SNI_UNINITIALIZED; @@ -40,6 +44,136 @@ public TdsParserStateObjectNative(TdsParser parser) 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, + ref string[] spns, + bool flushCache, + bool async, + bool fParallel, + TransparentNetworkResolutionState transparentNetworkResolutionState, + int totalTimeout, + SqlConnectionIPAddressPreference iPAddressPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + string serverSPN, + bool isIntegratedSecurity = false, + bool tlsFirst = false, + string hostNameInCertificate = "", + string serverCertificateFilename = "") + { + if (isIntegratedSecurity) + { + if (!string.IsNullOrEmpty(serverSPN)) + { + 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, transparentNetworkResolutionState, totalTimeout, + iPAddressPreference, cachedDNSInfo, hostNameInCertificate); + spns = 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"); @@ -74,6 +208,60 @@ protected override void RemovePacketFromPendingList(PacketHandle ptr) } } + 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) + { + // 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. + + // TODO: Make this a BID trace point! + RuntimeHelpers.PrepareConstrainedRegions(); + try + { } + finally + { + if (packetHandle != null) + { + packetHandle.Dispose(); + } + if (asyncAttnPacket != null) + { + asyncAttnPacket.Dispose(); + } + if (sessionHandle != null) + { + sessionHandle.Dispose(); + DecrementPendingCallbacks(true); // Will dispose of GC handle. + } + } + } + + DisposePacketCache(); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + 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) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/SspiContextProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/SspiContextProvider.cs index ff83422f10..60872cf416 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/SspiContextProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SSPI/SspiContextProvider.cs @@ -27,17 +27,6 @@ private protected virtual void Initialize() protected abstract bool GenerateSspiClientContext(ReadOnlySpan incomingBlob, IBufferWriter outgoingBlobWriter, SspiAuthenticationParameters authParams); - internal void SSPIData(ReadOnlySpan receivedBuff, IBufferWriter outgoingBlobWriter, string serverSpn) - { - using var _ = TrySNIEventScope.Create(nameof(SspiContextProvider)); - - if (!RunGenerateSspiClientContext(receivedBuff, outgoingBlobWriter, serverSpn)) - { - // If we've hit here, the SSPI context provider implementation failed to generate the SSPI context. - SSPIError(SQLMessage.SSPIGenerateError(), TdsEnums.GEN_CLIENT_CONTEXT); - } - } - internal void SSPIData(ReadOnlySpan receivedBuff, IBufferWriter outgoingBlobWriter, ReadOnlySpan serverSpns) { using var _ = TrySNIEventScope.Create(nameof(SspiContextProvider)); 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 a1b820bb0d..29381dbe27 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -12,6 +12,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Common; +using Microsoft.Data.ProviderBase; + #if NETFRAMEWORK using System.Runtime.ConstrainedExecution; #endif @@ -483,12 +485,39 @@ internal long TimeoutTime protected abstract PacketHandle EmptyReadPacket { get; } + protected abstract void CreateSessionHandle(TdsParserStateObject physicalConnection, bool async); + + internal abstract void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo); + + internal abstract void CreatePhysicalSNIHandle( + string serverName, + TimeoutTimer timeout, + out byte[] instanceName, + ref string[] spns, + bool flushCache, + bool async, + bool fParallel, + TransparentNetworkResolutionState transparentNetworkResolutionState, + int totalTimeout, + SqlConnectionIPAddressPreference iPAddressPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + string serverSPN, + bool isIntegratedSecurity = false, + bool tlsFirst = false, + string hostNameInCertificate = "", + string serverCertificateFilename = ""); + internal abstract PacketHandle GetResetWritePacket(int dataSize); protected abstract uint SniPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize); protected abstract bool CheckPacket(PacketHandle packet, TaskCompletionSource source); + internal abstract void Dispose(); + + protected abstract void FreeGcHandle(int remaining, bool release); + internal abstract bool IsFailedHandle(); internal abstract bool IsPacketEmpty(PacketHandle readPacket);