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 1309b646e7..cf53e8ecff 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
@@ -434,6 +434,8 @@ internal void Connect(ServerInfo serverInfo,
false,
true,
fParallel,
+ TransparentNetworkResolutionState.DisabledMode,
+ -1,
_connHandler.ConnectionOptions.IPAddressPreference,
FQDNforDNSCache,
ref _connHandler.pendingSQLDNSObject,
@@ -532,6 +534,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 5eb4464b54..86409c3b3b 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
@@ -21,7 +21,7 @@ internal abstract partial class TdsParserStateObject
// 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?");
@@ -57,40 +57,17 @@ internal TdsParserStateObject(TdsParser parser, TdsParserStateObject physicalCon
// General methods //
/////////////////////
- internal abstract void CreatePhysicalSNIHandle(
- string serverName,
- TimeoutTimer timeout,
- out byte[] instanceName,
- out ResolvedServerSpn resolvedSpn,
- 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 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 2b173fb087..0ebc36c06a 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
@@ -85,6 +85,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 9a9ae68105..bf7c4aebb7 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;
@@ -155,11 +158,13 @@ internal override void CreatePhysicalSNIHandle(
string serverName,
TimeoutTimer timeout,
out byte[] instanceName,
- out Microsoft.Data.SqlClient.ManagedSni.ResolvedServerSpn resolvedSpn,
+ out ManagedSni.ResolvedServerSpn resolvedSpn,
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);
resolvedSpn = new(serverSPN.TrimEnd());
}
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 b3231b0cee..746925de3c 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -450,6 +450,9 @@
Microsoft\Data\SqlClient\LocalAppContextSwitches.cs
+
+ Microsoft\Data\SqlClient\ManagedSni\ResolvedServerSpn.cs
+
Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs
@@ -924,7 +927,6 @@
-
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 756f2f044a..d65b0db83e 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
@@ -381,8 +381,11 @@ internal void Connect(ServerInfo serverInfo,
ThrowExceptionAndWarning(_physicalStateObj);
Debug.Fail("SNI returned status != success, but no error thrown?");
}
-
- string serverSpn = null;
+ 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)
@@ -401,17 +404,6 @@ internal void Connect(ServerInfo serverInfo,
{
_authenticationProvider = Connection._sspiContextProvider ?? _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
@@ -495,7 +487,7 @@ internal void Connect(ServerInfo serverInfo,
serverInfo.ExtendedServerName,
timeout,
out instanceName,
- ref serverSpn,
+ out var resolvedServerSpn,
false,
true,
fParallel,
@@ -503,7 +495,12 @@ internal void Connect(ServerInfo serverInfo,
totalTimeout,
_connHandler.ConnectionOptions.IPAddressPreference,
FQDNforDNSCache,
- hostNameInCertificate);
+ ref _connHandler.pendingSQLDNSObject,
+ serverInfo.ServerSPN,
+ integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated,
+ isTlsFirst,
+ hostNameInCertificate,
+ serverCertificateFilename);
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
{
@@ -544,7 +541,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)
{
@@ -587,15 +584,20 @@ internal void Connect(ServerInfo serverInfo,
serverInfo.ExtendedServerName,
timeout,
out instanceName,
- ref serverSpn,
+ out resolvedServerSpn,
true,
true,
fParallel,
transparentNetworkResolutionState,
totalTimeout,
_connHandler.ConnectionOptions.IPAddressPreference,
- serverInfo.ResolvedServerName,
- hostNameInCertificate);
+ FQDNforDNSCache,
+ ref _connHandler.pendingSQLDNSObject,
+ serverInfo.ServerSPN,
+ integratedSecurity,
+ isTlsFirst,
+ hostNameInCertificate,
+ serverCertificateFilename);
if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
{
@@ -609,7 +611,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(
@@ -631,7 +633,10 @@ internal void Connect(ServerInfo serverInfo,
}
SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake successful");
- _authenticationProvider?.Initialize(serverInfo, _physicalStateObj, this, serverSpn);
+ if (_authenticationProvider is { })
+ {
+ _authenticationProvider.Initialize(serverInfo, _physicalStateObj, this, resolvedServerSpn.Primary, resolvedServerSpn.Secondary);
+ }
if (_fMARS && marsCapable)
{
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
deleted file mode 100644
index d7957767fa..0000000000
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.netfx.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System;
-using System.Net;
-using System.Threading;
-using Interop.Windows.Sni;
-
-namespace Microsoft.Data.SqlClient
-{
- internal sealed partial class TdsParser
- {
- // 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: Remove.
- internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
- {
- return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj);
- // @TODO: CER Exception Handling was removed here (see GH#3581)
- }
- }
-}
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 4181012274..276eabce8c 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);
internal int DecrementPendingCallbacks(bool release)
@@ -141,11 +90,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
@@ -153,43 +98,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.
-
- 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 72c5356f14..9ffd372742 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,11 +5,15 @@
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.Security.Authentication;
using System.Threading.Tasks;
using Interop.Windows.Sni;
using Microsoft.Data.Common;
+using Microsoft.Data.ProviderBase;
namespace Microsoft.Data.SqlClient
{
@@ -17,21 +21,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;
@@ -41,6 +45,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,
+ 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 = 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);
+ 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");
@@ -75,6 +209,52 @@ 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.
+
+ 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/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
index 649087f32b..cdcd02efea 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs
@@ -1377,11 +1377,8 @@ private void RunParserReliably(BulkCopySimpleResultSet bulkCopyHandler = null)
internalConnection.ThreadHasParserLockForClose = true;
try
{
- #if NETFRAMEWORK
- _parser.RunReliably(RunBehavior.UntilDone, null, null, bulkCopyHandler, _stateObj);
- #else
- _parser.Run(RunBehavior.UntilDone, null, null, bulkCopyHandler, _stateObj);
- #endif
+ // @TODO: CER Exception Handling was removed here (see GH#3581)
+ _parser.Run(RunBehavior.UntilDone, null, null, bulkCopyHandler, _stateObj);
}
finally
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs
index 5113f08c92..04bd173f0c 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSessionPool.cs
@@ -215,24 +215,6 @@ internal string TraceString()
_cachedCount,
_cache.Count);
}
-
-#if NETFRAMEWORK
- internal void BestEffortCleanup()
- {
- for (int i = 0; i < _cache.Count; i++)
- {
- TdsParserStateObject session = _cache[i];
- if (session != null)
- {
- SNIHandle sessionHandle = session.Handle;
- if (sessionHandle != null)
- {
- sessionHandle.Dispose();
- }
- }
- }
- }
-#endif
}
}
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 473a6ffc00..343eaeca5a 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -13,6 +13,9 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Common;
+using Microsoft.Data.ProviderBase;
+using Microsoft.Data.SqlClient.ManagedSni;
+
#if NETFRAMEWORK
using System.Runtime.ConstrainedExecution;
#endif
@@ -506,12 +509,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,
+ out ResolvedServerSpn resolvedSpn,
+ 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