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..0847907f3b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -765,6 +765,9 @@
Microsoft\Data\SqlClient\TdsParser.cs
+
+ Microsoft\Data\SqlClient\TdsParser.SSPI.cs
+
Microsoft\Data\SqlClient\TdsParserHelperClasses.cs
@@ -826,8 +829,6 @@
-
-
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.RegisterEncoding.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.RegisterEncoding.cs
deleted file mode 100644
index 433e5007c7..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.RegisterEncoding.cs
+++ /dev/null
@@ -1,20 +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.Text;
-
-namespace Microsoft.Data.SqlClient
-{
- internal sealed partial class TdsParser
- {
- static TdsParser()
- {
- // For CoreCLR, we need to register the ANSI Code Page encoding provider before attempting to get an Encoding from a CodePage
- // For a default installation of SqlServer the encoding exchanged during Login is 1252. This encoding is not loaded by default
- // See Remarks at https://msdn.microsoft.com/en-us/library/system.text.encodingprovider(v=vs.110).aspx
- // SqlClient needs to register the encoding providers to make sure that even basic scenarios work with Sql Server.
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- }
- }
-}
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
deleted file mode 100644
index 5809f304fe..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ /dev/null
@@ -1,13774 +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.Buffers;
-using System.Buffers.Binary;
-using System.Collections.Generic;
-using System.Data;
-using System.Data.SqlTypes;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Security.Authentication;
-#if NETFRAMEWORK
-using System.Runtime.CompilerServices;
-#endif
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using Interop.Common.Sni;
-#if NETFRAMEWORK
-using Interop.Windows.Sni;
-#endif
-using Microsoft.Data.Common;
-using Microsoft.Data.ProviderBase;
-using Microsoft.Data.Sql;
-using Microsoft.Data.SqlClient.DataClassification;
-using Microsoft.Data.SqlClient.LocalDb;
-using Microsoft.Data.SqlClient.Server;
-#if NETFRAMEWORK
-using Microsoft.Data.SqlTypes;
-#endif
-using Microsoft.SqlServer.Server;
-
-namespace Microsoft.Data.SqlClient
-{
- // The TdsParser Object controls reading/writing to the netlib, parsing the tds,
- // and surfacing objects to the user.
- internal sealed partial class TdsParser
- {
- private static int _objectTypeCount; // EventSource counter
- private readonly SqlClientLogger _logger = new SqlClientLogger();
-
- private SspiContextProvider _authenticationProvider;
-
- internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
- internal int ObjectID => _objectID;
-
- ///
- /// Verify client encryption possibility.
- ///
- private bool ClientOSEncryptionSupport => TdsParserStateObjectFactory.Singleton.ClientOSEncryptionSupport;
-
- // Default state object for parser
- internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI.
-
- // Also, default logical stateObj and connection for MARS over SNI.
- internal TdsParserStateObject _pMarsPhysicalConObj = null; // With MARS enabled, cached physical stateObj and connection.
-
- // Must keep this around - especially for callbacks on pre-MARS
- // ReadAsync which will return if physical connection broken!
- //
- // Per Instance TDS Parser variables
- //
-
- // Constants
- private const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream
- private const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader
-
- // State variables
- internal TdsParserState _state = TdsParserState.Closed; // status flag for connection
-
- private string _server = ""; // name of server that the parser connects to
-
- internal volatile bool _fResetConnection = false; // flag to denote whether we are needing to call sp_reset
- internal volatile bool _fPreserveTransaction = false; // flag to denote whether we need to preserve the transaction when reseting
-
- private SqlCollation _defaultCollation; // default collation from the server
-
- private int _defaultCodePage;
-
- private int _defaultLCID;
-
- internal Encoding _defaultEncoding = null; // for sql character data
-
- private static EncryptionOptions s_sniSupportedEncryptionOption = TdsParserStateObjectFactory.Singleton.EncryptionOptions;
-
- private EncryptionOptions _encryptionOption = s_sniSupportedEncryptionOption;
-
- private SqlInternalTransaction _currentTransaction;
- private SqlInternalTransaction _pendingTransaction; // pending transaction for 2005 and beyond.
- // SQLHOT 483
- // need to hold on to the transaction id if distributed transaction merely rolls back without defecting.
- private long _retainedTransactionId = SqlInternalTransaction.NullTransactionId;
-
- // This counter is used for the entire connection to track the open result count for all
- // operations not under a transaction.
- private int _nonTransactedOpenResultCount = 0;
-
- // Connection reference
- private SqlInternalConnectionTds _connHandler;
-
- // Async/Mars variables
- private bool _fMARS = false;
-
- internal bool _loginWithFailover = false; // set to true while connect in failover mode so parser state object can adjust its logic
-
- internal AutoResetEvent _resetConnectionEvent = null; // Used to serialize executes and call reset on first execute only.
-
- internal TdsParserSessionPool _sessionPool = null; // initialized only when we're a MARS parser.
-
- // Version variables
-
- private bool _is2008 = false;
-
- private bool _is2012 = false;
-
- private bool _is2022 = false;
-
- // SqlStatistics
- private SqlStatistics _statistics = null;
-
- private bool _statisticsIsInTransaction = false;
-
- //
- // STATIC TDS Parser variables
- //
-
- // NIC address caching
- private static byte[] s_nicAddress; // cache the NIC address from the registry
-
- // textptr sequence
- private static readonly byte[] s_longDataHeader = { 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
- // XML metadata substitute sequence
- private static readonly byte[] s_xmlMetadataSubstituteSequence = { 0xe7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
- // JSON metadata substitute sequence
- private static readonly byte[] s_jsonMetadataSubstituteSequence = { 0xa7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
- // size of Guid (e.g. _clientConnectionId, ActivityId.Id)
- private const int GUID_SIZE = 16;
-
- // now data length is 1 byte
- // First bit is 1 indicating client support failover partner with readonly intent
- private static readonly byte[] s_featureExtDataAzureSQLSupportFeatureRequest = { 0x01 };
-
- // NOTE: You must take the internal connection's _parserLock before modifying this
- internal bool _asyncWrite = false;
-
- ///
- /// Get or set if column encryption is supported by the server.
- ///
- internal bool IsColumnEncryptionSupported { get; set; } = false;
-
- ///
- /// TCE version supported by the server
- ///
- internal byte TceVersionSupported { get; set; }
-
- ///
- /// Server supports retrying when the enclave CEKs sent by the client do not match what is needed for the query to run.
- ///
- internal bool AreEnclaveRetriesSupported { get; set; }
-
- ///
- /// Type of enclave being used by the server
- ///
- internal string EnclaveType { get; set; }
-
- internal bool isTcpProtocol { get; set; }
- internal string FQDNforDNSCache { get; set; }
-
- ///
- /// Get if data classification is enabled by the server.
- ///
- internal bool IsDataClassificationEnabled =>
- (DataClassificationVersion != TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED);
-
- ///
- /// Get or set data classification version. A value of 0 means that sensitivity classification is not enabled.
- ///
- internal int DataClassificationVersion { get; set; }
-
- private SqlCollation _cachedCollation;
-
- internal TdsParser(bool MARS, bool fAsynchronous)
- {
- _fMARS = MARS; // may change during Connect to pre 2005 servers
-
- _physicalStateObj = TdsParserStateObjectFactory.Singleton.CreateTdsParserStateObject(this);
- DataClassificationVersion = TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED;
- }
-
- internal SqlInternalConnectionTds Connection
- {
- get
- {
- return _connHandler;
- }
- }
-
- internal SqlInternalTransaction CurrentTransaction
- {
- get
- {
- return _currentTransaction;
- }
- set
- {
- Debug.Assert(value == _currentTransaction
- || _currentTransaction == null
- || value == null
- || (_currentTransaction != null && !_currentTransaction.IsLocal), "attempting to change current transaction?");
-
- // If there is currently a transaction active, we don't want to
- // change it; this can occur when there is a delegated transaction
- // and the user attempts to do an API begin transaction; in these
- // cases, it's safe to ignore the set.
- if ((_currentTransaction == null && value != null)
- || (_currentTransaction != null && value == null))
- {
- _currentTransaction = value;
- }
- }
- }
-
- internal int DefaultLCID
- {
- get
- {
- return _defaultLCID;
- }
- }
-
- internal EncryptionOptions EncryptionOptions
- {
- get
- {
- return _encryptionOption;
- }
- set
- {
- _encryptionOption = value;
- }
- }
-
- internal bool Is2008OrNewer
- {
- get
- {
- return _is2008;
- }
- }
-
- internal bool MARSOn
- {
- get
- {
- return _fMARS;
- }
- }
-
- internal SqlInternalTransaction PendingTransaction
- {
- get
- {
- return _pendingTransaction;
- }
- set
- {
- Debug.Assert(value != null, "setting a non-null PendingTransaction?");
- _pendingTransaction = value;
- }
- }
-
- internal string Server
- {
- get
- {
- return _server;
- }
- }
-
- internal TdsParserState State
- {
- get
- {
- return _state;
- }
- set
- {
- _state = value;
- }
- }
-
- internal SqlStatistics Statistics
- {
- get
- {
- return _statistics;
- }
- set
- {
- _statistics = value;
- }
- }
-
- private bool IncludeTraceHeader
- {
- get
- {
- return (_is2012 && SqlClientEventSource.Log.IsEnabled());
- }
- }
-
- internal int IncrementNonTransactedOpenResultCount()
- {
- // IMPORTANT - this increments the connection wide open result count for all
- // operations not under a transaction! Do not call if you intend to modify the
- // count for a transaction!
- Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
- int result = Interlocked.Increment(ref _nonTransactedOpenResultCount);
- return result;
- }
-
- internal void DecrementNonTransactedOpenResultCount()
- {
- // IMPORTANT - this decrements the connection wide open result count for all
- // operations not under a transaction! Do not call if you intend to modify the
- // count for a transaction!
- Interlocked.Decrement(ref _nonTransactedOpenResultCount);
- Debug.Assert(_nonTransactedOpenResultCount >= 0, "Unexpected result count state");
- }
-
- internal void ProcessPendingAck(TdsParserStateObject stateObj)
- {
- if (stateObj._attentionSent)
- {
- SqlClientEventSource.Log.TryTraceEvent("TdsParser.ProcessPendingAck | INFO | Connection Object Id {0}, State Obj Id {1}, Processing Attention.", _connHandler.ObjectID, stateObj.ObjectID);
- ProcessAttention(stateObj);
- }
- }
-
- internal void Connect(ServerInfo serverInfo,
- SqlInternalConnectionTds connHandler,
- TimeoutTimer timeout,
- SqlConnectionString connectionOptions,
- bool withFailover)
- {
- SqlConnectionEncryptOption encrypt = connectionOptions.Encrypt;
- bool isTlsFirst = (encrypt == SqlConnectionEncryptOption.Strict);
- bool trustServerCert = connectionOptions.TrustServerCertificate;
- bool integratedSecurity = connectionOptions.IntegratedSecurity;
- SqlAuthenticationMethod authType = connectionOptions.Authentication;
- string hostNameInCertificate = connectionOptions.HostNameInCertificate;
- string serverCertificateFilename = connectionOptions.ServerCertificate;
-
- if (_state != TdsParserState.Closed)
- {
- Debug.Fail("TdsParser.Connect called on non-closed connection!");
- return;
- }
-
- _connHandler = connHandler;
- _loginWithFailover = withFailover;
-
- // Clean up IsSQLDNSCachingSupported flag from previous status
- _connHandler.IsSQLDNSCachingSupported = false;
-
- uint sniStatus = TdsParserStateObjectFactory.Singleton.SNIStatus;
-
- if (sniStatus != TdsEnums.SNI_SUCCESS)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- _physicalStateObj.Dispose();
- 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());
- }
-
- // Encryption is not supported on SQL Local DB - disable it if they have only specified Mandatory
- if (connHandler.ConnectionOptions.LocalDBInstance != null && encrypt == SqlConnectionEncryptOption.Mandatory)
- {
- encrypt = SqlConnectionEncryptOption.Optional;
- SqlClientEventSource.Log.TryTraceEvent(" Encryption will be disabled as target server is a SQL Local DB instance.");
- }
-
- _authenticationProvider = null;
-
- // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
- if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
- {
- _authenticationProvider = Connection._sspiContextProvider ?? _physicalStateObj.CreateSspiContextProvider();
- SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication");
- }
-
- // if Strict encryption (i.e. isTlsFirst) is chosen trust server certificate should always be false.
- if (isTlsFirst)
- {
- trustServerCert = false;
- }
-
- byte[] instanceName = null;
-
- Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point.");
- _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.PreLoginBegin);
- _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
-
- bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover;
-
- FQDNforDNSCache = serverInfo.ResolvedServerName;
-
- int commaPos = FQDNforDNSCache.IndexOf(",", StringComparison.Ordinal);
- if (commaPos != -1)
- {
- FQDNforDNSCache = FQDNforDNSCache.Substring(0, commaPos);
- }
-
- _connHandler.pendingSQLDNSObject = null;
-
- // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server
- _physicalStateObj.CreatePhysicalSNIHandle(
- serverInfo.ExtendedServerName,
- timeout,
- out instanceName,
- out var resolvedServerSpn,
- false,
- true,
- fParallel,
- TransparentNetworkResolutionState.DisabledMode,
- -1,
- _connHandler.ConnectionOptions.IPAddressPreference,
- FQDNforDNSCache,
- ref _connHandler.pendingSQLDNSObject,
- serverInfo.ServerSPN,
- integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated,
- isTlsFirst,
- hostNameInCertificate,
- serverCertificateFilename);
-
- if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
-
- // Since connect failed, free the unmanaged connection memory.
- // HOWEVER - only free this after the netlib error was processed - if you
- // don't, the memory for the connection object might not be accurate and thus
- // a bad error could be returned (as it was when it was freed to early for me).
- _physicalStateObj.Dispose();
- SqlClientEventSource.Log.TryTraceEvent(" Login failure");
- ThrowExceptionAndWarning(_physicalStateObj);
- Debug.Fail("SNI returned status != success, but no error thrown?");
- }
-
- _server = serverInfo.ResolvedServerName;
-
- if (connHandler.PoolGroupProviderInfo != null)
- {
- // If we are pooling, check to see if we were processing an
- // alias which has changed, which means we need to clean out
- // the pool. See Webdata 104293.
- // This should not apply to routing, as it is not an alias change, routed connection
- // should still use VNN of AlwaysOn cluster as server for pooling purposes.
- connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.PreRoutingServerName == null ?
- serverInfo.ResolvedServerName : serverInfo.PreRoutingServerName);
- }
- _state = TdsParserState.OpenNotLoggedIn;
- _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfulWrite;
- _physicalStateObj.TimeoutTime = timeout.LegacyTimerExpire;
-
- bool marsCapable = false;
-
- _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.InitializeConnection);
- _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
-
- uint result = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId);
- Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
-
- if (_connHandler.pendingSQLDNSObject == null)
- {
- // for DNS Caching phase 1
- _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject);
- }
-
- if (!ClientOSEncryptionSupport)
- {
- //If encryption is required, an error will be thrown.
- if (encrypt != SqlConnectionEncryptOption.Optional)
- {
- _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
- _physicalStateObj.Dispose();
- ThrowExceptionAndWarning(_physicalStateObj);
- }
- _encryptionOption = EncryptionOptions.NOT_SUP;
- }
-
- // UNDONE - send "" for instance now, need to fix later
- SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake");
- SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, serverCertificateFilename);
-
- _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake);
- _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake);
-
- _physicalStateObj.SniContext = SniContext.Snix_PreLogin;
- SqlClientEventSource.Log.TryTraceEvent(" Consuming prelogin handshake");
- PreLoginHandshakeStatus status = ConsumePreLoginHandshake(
- encrypt,
- trustServerCert,
- integratedSecurity,
- out marsCapable,
- out _connHandler._fedAuthRequired,
- isTlsFirst,
- serverCertificateFilename);
-
- if (status == PreLoginHandshakeStatus.InstanceFailure)
- {
- SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake unsuccessful. Reattempting prelogin handshake");
- _physicalStateObj.Dispose(); // Close previous connection
-
- // On Instance failure re-connect and flush SNI named instance cache.
- _physicalStateObj.SniContext = SniContext.Snix_Connect;
- _physicalStateObj.CreatePhysicalSNIHandle(
- serverInfo.ExtendedServerName,
- timeout,
- out instanceName,
- out resolvedServerSpn,
- true,
- true,
- fParallel,
- TransparentNetworkResolutionState.DisabledMode,
- -1,
- _connHandler.ConnectionOptions.IPAddressPreference,
- FQDNforDNSCache,
- ref _connHandler.pendingSQLDNSObject,
- serverInfo.ServerSPN,
- integratedSecurity,
- isTlsFirst,
- hostNameInCertificate,
- serverCertificateFilename);
-
- if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- SqlClientEventSource.Log.TryTraceEvent(" Login failure");
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- uint retCode = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId);
-
- Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId");
- SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake");
-
- if (_connHandler.pendingSQLDNSObject == null)
- {
- // for DNS Caching phase 1
- _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject);
- }
-
- SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, serverCertificateFilename);
- status = ConsumePreLoginHandshake(
- encrypt,
- trustServerCert,
- integratedSecurity,
- out marsCapable,
- out _connHandler._fedAuthRequired,
- isTlsFirst,
- serverCertificateFilename);
-
- // Don't need to check for 7.0 failure, since we've already consumed
- // one pre-login packet and know we are connecting to 2000.
- if (status == PreLoginHandshakeStatus.InstanceFailure)
- {
- SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake unsuccessful. Login failure");
- throw SQL.InstanceFailure();
- }
- }
- SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake successful");
-
- if (_authenticationProvider is { })
- {
- _authenticationProvider.Initialize(serverInfo, _physicalStateObj, this, resolvedServerSpn.Primary, resolvedServerSpn.Secondary);
- }
-
- if (_fMARS && marsCapable)
- {
- // if user explicitly disables mars or mars not supported, don't create the session pool
- _sessionPool = new TdsParserSessionPool(this);
- }
- else
- {
- _fMARS = false;
- }
- return;
- }
-
- internal void RemoveEncryption()
- {
- Debug.Assert(_encryptionOption == EncryptionOptions.LOGIN, "Invalid encryption option state");
-
- uint error = _physicalStateObj.DisableSsl();
-
- if (error != TdsEnums.SNI_SUCCESS)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- // create a new packet encryption changes the internal packet size Bug# 228403
- _physicalStateObj.ClearAllWritePackets();
- }
-
- internal void EnableMars()
- {
- if (_fMARS)
- {
- // Cache physical stateObj and connection.
- _pMarsPhysicalConObj = _physicalStateObj;
-
- if (LocalAppContextSwitches.UseManagedNetworking)
- _pMarsPhysicalConObj.IncrementPendingCallbacks();
-
- uint info = 0;
- uint error = _pMarsPhysicalConObj.EnableMars(ref info);
-
- if (error != TdsEnums.SNI_SUCCESS)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- error = _pMarsPhysicalConObj.PostReadAsyncForMars(_physicalStateObj);
- if (error != TdsEnums.SNI_SUCCESS_IO_PENDING)
- {
- Debug.Assert(error != TdsEnums.SNI_SUCCESS, "Unexpected successful read async on physical connection before enabling MARS!");
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- _physicalStateObj = CreateSession(); // Create and open default MARS stateObj and connection.
- }
- }
-
- internal TdsParserStateObject CreateSession()
- {
- TdsParserStateObject session = TdsParserStateObjectFactory.Singleton.CreateSessionObject(this, _pMarsPhysicalConObj, true);
- SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} created session {1}", ObjectID, session.ObjectID);
- return session;
- }
-
- internal TdsParserStateObject GetSession(object owner)
- {
- TdsParserStateObject session = null;
-
- // TODO: Ideally, we would not care what we do here -- the session pooler would know whether it should have either one (non-MARS) or many (MARS) sessions.
-
- if (MARSOn)
- {
- session = _sessionPool.GetSession(owner);
-
- Debug.Assert(!session.HasPendingData, "pending data on a pooled MARS session");
- SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} getting session {1} from pool", ObjectID, session.ObjectID);
- }
- else
- {
- session = _physicalStateObj;
- SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} getting physical session {1}", ObjectID, session.ObjectID);
- }
- Debug.Assert(session._outputPacketNumber == 1, "The packet number is expected to be 1");
- return session;
- }
-
- internal void PutSession(TdsParserStateObject session)
- {
- session.AssertStateIsClean();
-
- if (MARSOn)
- {
- // This will take care of disposing if the parser is closed
- _sessionPool.PutSession(session);
- }
- else if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))
- {
- // Parser is closed\broken - dispose the stateObj
- Debug.Assert(session == _physicalStateObj, "MARS is off, but session to close is not the _physicalStateObj");
- _physicalStateObj.SniContext = SniContext.Snix_Close;
-#if DEBUG
- _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
-#endif
- _physicalStateObj.Dispose();
- }
- else
- {
- // Non-MARS, and session is ok - remove its owner
- _physicalStateObj.Owner = null;
- }
- }
-
- private void SendPreLoginHandshake(
- byte[] instanceName,
- SqlConnectionEncryptOption encrypt,
- bool integratedSecurity,
- string serverCertificateFilename)
- {
- if (encrypt == SqlConnectionEncryptOption.Strict)
- {
- //Always validate the certificate when in strict encryption mode
- uint info = TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE | TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE | TdsEnums.SNI_SSL_SEND_ALPN_EXTENSION;
-
- EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename);
-
- // Since encryption has already been negotiated, we need to set encryption not supported in
- // prelogin so that we don't try to negotiate encryption again during ConsumePreLoginHandshake.
- _encryptionOption = EncryptionOptions.NOT_SUP;
- }
-
- // PreLoginHandshake buffer consists of:
- // 1) Standard header, with type = MT_PRELOGIN
- // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length)
- // 3) Consecutive data blocks for each option
-
- // NOTE: packet data needs to be big endian - not the standard little endian used by
- // the rest of the parser.
-
- _physicalStateObj._outputMessageType = TdsEnums.MT_PRELOGIN;
-
- // Initialize option offset into payload buffer
- // 5 bytes for each option (1 byte length, 2 byte offset, 2 byte payload length)
- int offset = (int)PreLoginOptions.NUMOPT * 5 + 1;
-
- byte[] payload = new byte[(int)PreLoginOptions.NUMOPT * 5 + TdsEnums.MAX_PRELOGIN_PAYLOAD_LENGTH];
- int payloadLength = 0;
-
- // UNDONE - need to do some length verification to ensure packet does not
- // get too big!!! Not beyond it's max length!
-
- for (int option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++)
- {
- int optionDataSize = 0;
-
- // Fill in the option
- _physicalStateObj.WriteByte((byte)option);
-
- // Fill in the offset of the option data
- _physicalStateObj.WriteByte((byte)((offset & 0xff00) >> 8)); // send upper order byte
- _physicalStateObj.WriteByte((byte)(offset & 0x00ff)); // send lower order byte
-
- switch (option)
- {
- case (int)PreLoginOptions.VERSION:
- Version systemDataVersion = ADP.GetAssemblyVersion();
-
- // Major and minor
- payload[payloadLength++] = (byte)(systemDataVersion.Major & 0xff);
- payload[payloadLength++] = (byte)(systemDataVersion.Minor & 0xff);
-
- // Build (Big Endian)
- payload[payloadLength++] = (byte)((systemDataVersion.Build & 0xff00) >> 8);
- payload[payloadLength++] = (byte)(systemDataVersion.Build & 0xff);
-
- // Sub-build (Little Endian)
- payload[payloadLength++] = (byte)(systemDataVersion.Revision & 0xff);
- payload[payloadLength++] = (byte)((systemDataVersion.Revision & 0xff00) >> 8);
- offset += 6;
- optionDataSize = 6;
- break;
-
- case (int)PreLoginOptions.ENCRYPT:
- if (_encryptionOption == EncryptionOptions.NOT_SUP)
- {
- //If OS doesn't support encryption and encryption is not required, inform server "not supported" by client.
- payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
- }
- else
- {
- // Else, inform server of user request.
- if (encrypt == SqlConnectionEncryptOption.Mandatory)
- {
- payload[payloadLength] = (byte)EncryptionOptions.ON;
- _encryptionOption = EncryptionOptions.ON;
- }
- else
- {
- payload[payloadLength] = (byte)EncryptionOptions.OFF;
- _encryptionOption = EncryptionOptions.OFF;
- }
- }
-
- payloadLength += 1;
- offset += 1;
- optionDataSize = 1;
- break;
-
- case (int)PreLoginOptions.INSTANCE:
- int i = 0;
-
- while (instanceName[i] != 0)
- {
- payload[payloadLength] = instanceName[i];
- payloadLength++;
- i++;
- }
-
- payload[payloadLength] = 0; // null terminate
- payloadLength++;
- i++;
-
- offset += i;
- optionDataSize = i;
- break;
-
- case (int)PreLoginOptions.THREADID:
- int threadID = TdsParserStaticMethods.GetCurrentThreadIdForTdsLoginOnly();
-
- payload[payloadLength++] = (byte)((0xff000000 & threadID) >> 24);
- payload[payloadLength++] = (byte)((0x00ff0000 & threadID) >> 16);
- payload[payloadLength++] = (byte)((0x0000ff00 & threadID) >> 8);
- payload[payloadLength++] = (byte)(0x000000ff & threadID);
- offset += 4;
- optionDataSize = 4;
- break;
-
- case (int)PreLoginOptions.MARS:
- payload[payloadLength++] = (byte)(_fMARS ? 1 : 0);
- offset += 1;
- optionDataSize += 1;
- break;
-
- case (int)PreLoginOptions.TRACEID:
- SerializeGuid(_connHandler._clientConnectionId, payload.AsSpan(payloadLength, GUID_SIZE));
- payloadLength += GUID_SIZE;
- offset += GUID_SIZE;
- optionDataSize = GUID_SIZE;
-
- ActivityCorrelator.ActivityId actId = ActivityCorrelator.Next();
- SerializeGuid(actId.Id, payload.AsSpan(payloadLength, GUID_SIZE));
- payloadLength += GUID_SIZE;
- payload[payloadLength++] = (byte)(0x000000ff & actId.Sequence);
- payload[payloadLength++] = (byte)((0x0000ff00 & actId.Sequence) >> 8);
- payload[payloadLength++] = (byte)((0x00ff0000 & actId.Sequence) >> 16);
- payload[payloadLength++] = (byte)((0xff000000 & actId.Sequence) >> 24);
- int actIdSize = GUID_SIZE + sizeof(uint);
- offset += actIdSize;
- optionDataSize += actIdSize;
- SqlClientEventSource.Log.TryTraceEvent(" ClientConnectionID {0}, ActivityID {1}", _connHandler?._clientConnectionId, actId);
- break;
-
- case (int)PreLoginOptions.FEDAUTHREQUIRED:
- payload[payloadLength++] = 0x01;
- offset += 1;
- optionDataSize += 1;
- break;
-
- default:
- Debug.Fail("UNKNOWN option in SendPreLoginHandshake");
- break;
- }
-
- // Write data length
- _physicalStateObj.WriteByte((byte)((optionDataSize & 0xff00) >> 8));
- _physicalStateObj.WriteByte((byte)(optionDataSize & 0x00ff));
- }
-
- // Write out last option - to let server know the second part of packet completed
- _physicalStateObj.WriteByte((byte)PreLoginOptions.LASTOPT);
-
- // Write out payload
- _physicalStateObj.WriteByteArray(payload, payloadLength, 0);
-
- // Flush packet
- _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH);
- }
-
- private void EnableSsl(uint info, SqlConnectionEncryptOption encrypt, bool integratedSecurity, string serverCertificateFilename)
- {
- uint error = 0;
-
- if (encrypt && !integratedSecurity)
- {
- // optimization: in case of SQL Authentication and encryption in TDS, set SNI_SSL_IGNORE_CHANNEL_BINDINGS
- // to let SNI know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
- // This applies to Native SNI
- info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS;
- }
-
- error = _physicalStateObj.EnableSsl(ref info, encrypt == SqlConnectionEncryptOption.Strict, serverCertificateFilename);
-
- if (error != TdsEnums.SNI_SUCCESS)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- SslProtocols protocol = 0;
-
- // in the case where an async connection is made, encryption is used and Windows Authentication is used,
- // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its
- // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete
- // before calling SNISecGenClientContext).
-#if NET
- if (OperatingSystem.IsWindows())
-#endif
- {
- error = _physicalStateObj.WaitForSSLHandShakeToComplete(out protocol);
- if (error != TdsEnums.SNI_SUCCESS)
- {
- _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
- ThrowExceptionAndWarning(_physicalStateObj);
- }
- }
-
- string warningMessage = protocol.GetProtocolWarning();
- if (!string.IsNullOrEmpty(warningMessage))
- {
- if (!encrypt && LocalAppContextSwitches.SuppressInsecureTlsWarning)
- {
- // Skip console warning
- SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(EnableSsl), SqlClientLogger.LogLevel.Warning, warningMessage);
- }
- else
- {
- // This logs console warning of insecure protocol in use.
- _logger.LogWarning(nameof(TdsParser), nameof(EnableSsl), warningMessage);
- }
- }
-
- // create a new packet encryption changes the internal packet size Bug# 228403
- _physicalStateObj.ClearAllWritePackets();
- }
-
- private PreLoginHandshakeStatus ConsumePreLoginHandshake(
- SqlConnectionEncryptOption encrypt,
- bool trustServerCert,
- bool integratedSecurity,
- out bool marsCapable,
- out bool fedAuthRequired,
- bool tlsFirst,
- string serverCertificateFilename)
- {
- // Assign default values
- marsCapable = _fMARS;
- fedAuthRequired = false;
- Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
- TdsOperationStatus result = _physicalStateObj.TryReadNetworkPacket();
- if (result != TdsOperationStatus.Done)
- {
- throw SQL.SynchronousCallMayNotPend();
- }
-
- if (_physicalStateObj._inBytesRead == 0)
- {
- // If the server did not respond then something has gone wrong and we need to close the connection
- _physicalStateObj.AddError(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.PreloginError(), "", 0));
- _physicalStateObj.Dispose();
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- if (_physicalStateObj.TryProcessHeader() != TdsOperationStatus.Done)
- {
- throw SQL.SynchronousCallMayNotPend();
- }
-
- if (_physicalStateObj._inBytesPacket > TdsEnums.MAX_PACKET_SIZE || _physicalStateObj._inBytesPacket <= 0)
- {
- throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream);
- }
- byte[] payload = new byte[_physicalStateObj._inBytesPacket];
-
- Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
- result = _physicalStateObj.TryReadByteArray(payload, payload.Length);
- if (result != TdsOperationStatus.Done)
- {
- throw SQL.SynchronousCallMayNotPend();
- }
-
- if (payload[0] == 0xaa)
- {
- // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
- // is not supported. SQL BU DT 296425
- throw SQL.InvalidSQLServerVersionUnknown();
- }
-
- int offset = 0;
- int payloadOffset = 0;
- int payloadLength = 0;
- int option = payload[offset++];
- bool serverSupportsEncryption = false;
-
- while (option != (byte)PreLoginOptions.LASTOPT)
- {
- switch (option)
- {
- case (int)PreLoginOptions.VERSION:
- payloadOffset = payload[offset++] << 8 | payload[offset++];
- payloadLength = payload[offset++] << 8 | payload[offset++];
-
- byte majorVersion = payload[payloadOffset];
- byte minorVersion = payload[payloadOffset + 1];
- int level = (payload[payloadOffset + 2] << 8) |
- payload[payloadOffset + 3];
- break;
-
- case (int)PreLoginOptions.ENCRYPT:
- if (tlsFirst)
- {
- // Can skip/ignore this option if we are doing TDS 8.
- offset += 4;
- break;
- }
-
- payloadOffset = payload[offset++] << 8 | payload[offset++];
- payloadLength = payload[offset++] << 8 | payload[offset++];
-
- EncryptionOptions serverOption = ((EncryptionOptions)payload[payloadOffset]) & EncryptionOptions.OPTIONS_MASK;
-
- /* internal enum EncryptionOptions {
- OFF,
- ON,
- NOT_SUP,
- REQ,
- LOGIN,
- OPTIONS_MASK = 0x3f
- } */
-
- // Any response other than NOT_SUP means the server supports encryption.
- serverSupportsEncryption = serverOption != EncryptionOptions.NOT_SUP;
-
- switch (_encryptionOption)
- {
- case (EncryptionOptions.OFF):
- if (serverOption == EncryptionOptions.OFF)
- {
- // Only encrypt login.
- _encryptionOption = EncryptionOptions.LOGIN;
- }
- else if (serverOption == EncryptionOptions.REQ)
- {
- // Encrypt all.
- _encryptionOption = EncryptionOptions.ON;
- }
- // NOT_SUP: No encryption.
- break;
-
- case (EncryptionOptions.NOT_SUP):
- if (serverOption == EncryptionOptions.REQ)
- {
- // Server requires encryption, but client does not support it.
- _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
- _physicalStateObj.Dispose();
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- break;
- default:
- // Any other client option needs encryption
- if (serverOption == EncryptionOptions.NOT_SUP)
- {
- _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
- _physicalStateObj.Dispose();
- ThrowExceptionAndWarning(_physicalStateObj);
- }
- break;
- }
-
- break;
-
- case (int)PreLoginOptions.INSTANCE:
- payloadOffset = payload[offset++] << 8 | payload[offset++];
- payloadLength = payload[offset++] << 8 | payload[offset++];
-
- byte ERROR_INST = 0x1;
- byte instanceResult = payload[payloadOffset];
-
- if (instanceResult == ERROR_INST)
- {
- // Check if server says ERROR_INST. That either means the cached info
- // we used to connect is not valid or we connected to a named instance
- // listening on default params.
- return PreLoginHandshakeStatus.InstanceFailure;
- }
-
- break;
-
- case (int)PreLoginOptions.THREADID:
- // DO NOTHING FOR THREADID
- offset += 4;
- break;
-
- case (int)PreLoginOptions.MARS:
- payloadOffset = payload[offset++] << 8 | payload[offset++];
- payloadLength = payload[offset++] << 8 | payload[offset++];
-
- marsCapable = (payload[payloadOffset] == 0 ? false : true);
-
- Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
- break;
-
- case (int)PreLoginOptions.TRACEID:
- // DO NOTHING FOR TRACEID
- offset += 4;
- break;
-
- case (int)PreLoginOptions.FEDAUTHREQUIRED:
- payloadOffset = payload[offset++] << 8 | payload[offset++];
- payloadLength = payload[offset++] << 8 | payload[offset++];
-
- // Only 0x00 and 0x01 are accepted values from the server.
- if (payload[payloadOffset] != 0x00 && payload[payloadOffset] != 0x01)
- {
- SqlClientEventSource.Log.TryTraceEvent(" {0}, " +
- "Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was {1}.", ObjectID, (int)payload[payloadOffset]);
- throw SQL.ParsingErrorValue(ParsingErrorState.FedAuthRequiredPreLoginResponseInvalidValue, (int)payload[payloadOffset]);
- }
-
- // We must NOT use the response for the FEDAUTHREQUIRED PreLogin option, if the connection string option
- // was not using the new Authentication keyword or in other words, if Authentication=NotSpecified
- // Or AccessToken is not null, mean token based authentication is used.
- if ((_connHandler.ConnectionOptions != null
- && _connHandler.ConnectionOptions.Authentication != SqlAuthenticationMethod.NotSpecified)
- || _connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null)
- {
- fedAuthRequired = payload[payloadOffset] == 0x01 ? true : false;
- }
- break;
-
- default:
- Debug.Fail("UNKNOWN option in ConsumePreLoginHandshake, option:" + option);
-
- // DO NOTHING FOR THESE UNKNOWN OPTIONS
- offset += 4;
-
- break;
- }
-
- if (offset < payload.Length)
- {
- option = payload[offset++];
- }
- else
- {
- break;
- }
- }
-
- if (_encryptionOption == EncryptionOptions.ON ||
- _encryptionOption == EncryptionOptions.LOGIN)
- {
- if (!serverSupportsEncryption)
- {
- _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
- _physicalStateObj.Dispose();
- ThrowExceptionAndWarning(_physicalStateObj);
- }
-
- // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server.
- bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) ||
- ((_connHandler._accessTokenInBytes != null || _connHandler._accessTokenCallback != null) && !trustServerCert);
-
- uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
- | TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE;
-
- EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename);
- }
-
- return PreLoginHandshakeStatus.Successful;
- }
-
- internal void Deactivate(bool connectionIsDoomed)
- {
- // Called when the connection that owns us is deactivated.
- SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} deactivating", ObjectID);
- if (SqlClientEventSource.Log.IsStateDumpEnabled())
- {
- SqlClientEventSource.Log.StateDumpEvent(" {0} {1}", ObjectID, TraceString());
- }
-
- if (MARSOn)
- {
- _sessionPool.Deactivate();
- }
-
- Debug.Assert(connectionIsDoomed || _pendingTransaction == null, "pending transaction at disconnect?");
-
- if (!connectionIsDoomed && _physicalStateObj != null)
- {
- if (_physicalStateObj.HasPendingData)
- {
- DrainData(_physicalStateObj);
- }
-
- if (_physicalStateObj.HasOpenResult)
- { // SQL BU DT 383773 - need to decrement openResultCount for all pending operations.
- _physicalStateObj.DecrementOpenResultCount();
- }
- }
-
- // Any active, non-distributed transaction must be rolled back. We
- // need to wait for distributed transactions to be completed by the
- // transaction manager -- we don't want to automatically roll them
- // back.
- //
- // Note that when there is a transaction delegated to this connection,
- // we will defer the deactivation of this connection until the
- // transaction manager completes the transaction.
- SqlInternalTransaction currentTransaction = CurrentTransaction;
-
- if (currentTransaction != null && currentTransaction.HasParentTransaction)
- {
- currentTransaction.CloseFromConnection();
- Debug.Assert(CurrentTransaction == null, "rollback didn't clear current transaction?");
- }
-
- Statistics = null; // must come after CleanWire or we won't count the stuff that happens there...
- }
-
- // Used to close the connection and then free the memory allocated for the netlib connection.
- internal void Disconnect()
- {
- if (_sessionPool != null)
- {
- // MARSOn may be true, but _sessionPool not yet created
- _sessionPool.Dispose();
- }
-
- // Can close the connection if its open or broken
- if (_state != TdsParserState.Closed)
- {
- //benign assert - the user could close the connection before consuming all the data
- //Debug.Assert(_physicalStateObj._inBytesUsed == _physicalStateObj._inBytesRead && _physicalStateObj._outBytesUsed == _physicalStateObj._inputHeaderLen, "TDSParser closed with data not fully sent or consumed.");
-
- _state = TdsParserState.Closed;
-
- try
- {
- // If the _physicalStateObj has an owner, we will delay the disposal until the owner is finished with it
- if (!_physicalStateObj.HasOwner)
- {
- _physicalStateObj.SniContext = SniContext.Snix_Close;
-#if DEBUG
- _physicalStateObj.InvalidateDebugOnlyCopyOfSniContext();
-#endif
- _physicalStateObj.Dispose();
- }
- else
- {
- // Remove the "initial" callback (this will allow the stateObj to be GC collected if need be)
- _physicalStateObj.DecrementPendingCallbacks(false);
- }
-
- // Not allocated until MARS is actually enabled in SNI.
- if (_pMarsPhysicalConObj != null)
- {
- _pMarsPhysicalConObj.Dispose();
- }
- }
- finally
- {
- _pMarsPhysicalConObj = null;
- }
- }
-
- _resetConnectionEvent?.Dispose();
- _resetConnectionEvent = null;
-
- _defaultEncoding = null;
- _defaultCollation = null;
- }
-
- // Fires a single InfoMessageEvent
- private void FireInfoMessageEvent(SqlConnection connection, SqlCommand command, TdsParserStateObject stateObj, SqlError error)
- {
- string serverVersion = null;
-
- Debug.Assert(connection != null && _connHandler.Connection == connection);
-
- if (_state == TdsParserState.OpenLoggedIn)
- {
- serverVersion = _connHandler.ServerVersion;
- }
-
- SqlErrorCollection sqlErs = new SqlErrorCollection();
-
- sqlErs.Add(error);
-
- SqlException exc = SqlException.CreateException(sqlErs, serverVersion, _connHandler, innerException: null, batchCommand: command?.GetCurrentBatchCommand());
-
- bool notified;
- connection.OnInfoMessage(new SqlInfoMessageEventArgs(exc), out notified);
- if (notified)
- {
- // observable side-effects, no retry
- stateObj._syncOverAsync = true;
- }
- return;
- }
-
- internal void DisconnectTransaction(SqlInternalTransaction internalTransaction)
- {
- Debug.Assert(_currentTransaction != null && _currentTransaction == internalTransaction, "disconnecting different transaction");
-
- if (_currentTransaction != null && _currentTransaction == internalTransaction)
- {
- _currentTransaction = null;
- }
- }
-
- internal void RollbackOrphanedAPITransactions()
- {
- // Any active, non-distributed transaction must be rolled back.
- SqlInternalTransaction currentTransaction = CurrentTransaction;
-
- if (currentTransaction != null && currentTransaction.HasParentTransaction && currentTransaction.IsOrphaned)
- {
- currentTransaction.CloseFromConnection();
- Debug.Assert(CurrentTransaction == null, "rollback didn't clear current transaction?");
- }
- }
-
- internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command = null, bool callerHasConnectionLock = false, bool asyncClose = false)
- {
- Debug.Assert(!callerHasConnectionLock || _connHandler._parserLock.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken");
-
- SqlException exception = null;
- bool breakConnection;
-
- // This function should only be called when there was an error or warning. If there aren't any
- // errors, the handler will be called for the warning(s). If there was an error, the warning(s) will
- // be copied to the end of the error collection so that the user may see all the errors and also the
- // warnings that occurred.
- // can be deleted)
- //_errorAndWarningsLock lock is implemented inside GetFullErrorAndWarningCollection
- SqlErrorCollection temp = stateObj.GetFullErrorAndWarningCollection(out breakConnection);
-
- if (temp.Count == 0)
- {
- SqlClientEventSource.Log.TryTraceEvent(" Potential multi-threaded misuse of connection, unexpectedly empty warnings/errors under lock {0}", ObjectID);
- }
- Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: null errors collection!");
- Debug.Assert(temp.Count > 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!");
- Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!");
-
- // Don't break the connection if it is already closed
- breakConnection &= (TdsParserState.Closed != _state);
- if (breakConnection)
- {
- if ((_state == TdsParserState.OpenNotLoggedIn) && (_connHandler.ConnectionOptions.MultiSubnetFailover || _loginWithFailover) && (temp.Count == 1) && ((temp[0].Number == TdsEnums.TIMEOUT_EXPIRED) || (temp[0].Number == TdsEnums.SNI_WAIT_TIMEOUT)))
- {
- // DevDiv2 Bug 459546: With "MultiSubnetFailover=yes" in the Connection String, SQLClient incorrectly throws a Timeout using shorter time slice (3-4 seconds), not honoring the actual 'Connect Timeout'
- // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/459546
- // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously)
- // However, when timeout occurs we need to not doom the internal connection and also to mark the TdsParser as closed such that the login will be will retried
- breakConnection = false;
- Disconnect();
- }
- else
- {
- _state = TdsParserState.Broken;
- }
- }
-
- if (temp != null && temp.Count > 0)
- {
- // Construct the exception now that we've collected all the errors
- string serverVersion = null;
- if (_state == TdsParserState.OpenLoggedIn)
- {
- serverVersion = _connHandler.ServerVersion;
- }
-
- if (temp.Count == 1 && temp[0].Exception != null)
- {
- exception = SqlException.CreateException(temp, serverVersion, _connHandler, temp[0].Exception, command?.GetBatchCommand(temp[0].BatchIndex));
- }
- else
- {
- SqlBatchCommand batchCommand = null;
- if (temp[0]?.BatchIndex is var index and >= 0 && command is not null)
- {
- batchCommand = command.GetBatchCommand(index.Value);
- }
- exception = SqlException.CreateException(temp, serverVersion, _connHandler, innerException: null, batchCommand: batchCommand);
- }
- }
-
- if (exception != null)
- {
- if (breakConnection)
- {
- // report exception to pending async operation
- // before OnConnectionClosed overrides the exception
- // due to connection close notification through references
- TaskCompletionSource