[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlConnection.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnection.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
8
9 [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Data.DataSetExtensions, PublicKey="+AssemblyRef.EcmaPublicKeyFull)] // DevDiv Bugs 92166
10
11 namespace System.Data.SqlClient
12 {
13     using System;
14     using System.Collections;
15     using System.Collections.Concurrent;
16     using System.Collections.Generic;
17     using System.Collections.ObjectModel;
18     using System.Configuration.Assemblies;
19     using System.ComponentModel;
20     using System.Data;
21     using System.Data.Common;
22     using System.Data.ProviderBase;
23     using System.Data.Sql;
24     using System.Data.SqlTypes;
25     using System.Diagnostics;
26     using System.Globalization;
27     using System.IO;
28     using System.Linq;
29     using System.Runtime.CompilerServices;
30     using System.Runtime.ConstrainedExecution;
31     using System.Runtime.InteropServices;
32     using System.Runtime.Remoting;
33     using System.Runtime.Serialization.Formatters;
34     using System.Text;
35     using System.Threading;
36     using System.Threading.Tasks;
37     using System.Security;
38     using System.Security.Permissions;
39     using System.Reflection;
40     using System.Runtime.Versioning;
41     
42     using Microsoft.SqlServer.Server;
43     using System.Security.Principal;
44     using System.Diagnostics.CodeAnalysis;
45
46     [DefaultEvent("InfoMessage")]
47     public sealed partial class SqlConnection: DbConnection, ICloneable {
48
49         static private readonly object EventInfoMessage = new object();
50
51         // System column encryption key store providers are added by default
52         static private readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider> _SystemColumnEncryptionKeyStoreProviders
53             = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
54         {
55             {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()},
56             {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()},
57             {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()}
58         };
59
60         /// <summary>
61         /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary.
62         /// Custom provider list can only supplied once per application.
63         /// </summary>
64         static private ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _CustomColumnEncryptionKeyStoreProviders;
65
66         // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
67         static private readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object();
68
69         /// <summary>
70         /// Dictionary object holding trusted key paths for various SQL Servers.
71         /// Key to the dictionary is a SQL Server Name
72         /// IList contains a list of trusted key paths.
73         /// </summary>
74         static private readonly ConcurrentDictionary<string, IList<string>> _ColumnEncryptionTrustedMasterKeyPaths
75             = new ConcurrentDictionary<string, IList<string>>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
76                                                             capacity: 1,
77                                                             comparer: StringComparer.OrdinalIgnoreCase);
78
79         [
80         DefaultValue(null),
81         ResCategoryAttribute(Res.DataCategory_Data),
82         ResDescriptionAttribute(Res.TCE_SqlConnection_TrustedColumnMasterKeyPaths),
83         ]
84         static public IDictionary<string, IList<string>> ColumnEncryptionTrustedMasterKeyPaths
85         {
86             get
87             {
88                 return _ColumnEncryptionTrustedMasterKeyPaths;
89             }
90         }
91         
92         /// <summary>
93         /// This function should only be called once in an app. This does shallow copying of the dictionary so that 
94         /// the app cannot alter the custom provider list once it has been set.
95         /// 
96         /// Example:
97         /// 
98         /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
99         /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
100         /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
101         /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
102         /// </summary>
103         /// <param name="customProviders">Custom column encryption key provider dictionary</param>
104         static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders)
105         {
106
107             // Return when the provided dictionary is null.
108             if (customProviders == null)
109             {
110                 throw SQL.NullCustomKeyStoreProviderDictionary();
111             }
112
113             // Validate that custom provider list doesn't contain any of system provider list
114             foreach (string key in customProviders.Keys)
115             {
116                 // Validate the provider name
117                 //
118                 // Check for null or empty
119                 if (string.IsNullOrWhiteSpace(key))
120                 {
121                     throw SQL.EmptyProviderName();
122                 }
123
124                 // Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
125                 if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase)) 
126                 {
127                     throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
128                 }
129
130                 // Validate the provider value
131                 if (customProviders[key] == null)
132                 {
133                     throw SQL.NullProviderValue(key);
134                 }
135             }
136
137             lock (_CustomColumnEncryptionKeyProvidersLock)
138             {
139                 // Provider list can only be set once
140                 if (_CustomColumnEncryptionKeyStoreProviders != null)
141                 {
142                     throw SQL.CanOnlyCallOnce();
143                 }
144
145                 // Create a temporary dictionary and then add items from the provided dictionary.
146                 // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
147                 // in the provided customerProviders dictionary.
148                 Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customColumnEncryptionKeyStoreProviders =
149                     new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(customProviders, StringComparer.OrdinalIgnoreCase);
150
151                 // Set the dictionary to the ReadOnly dictionary.
152                 _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider>(customColumnEncryptionKeyStoreProviders);
153             }
154         }
155
156         /// <summary>
157         /// This function walks through both system and custom column encryption key store providers and returns an object if found.
158         /// </summary>
159         /// <param name="providerName">Provider Name to be searched in System Provider diction and Custom provider dictionary.</param>
160         /// <param name="columnKeyStoreProvider">If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.</param>
161         /// <returns>true if the provider is found, else returns false</returns>
162         static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
163             Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
164
165             // Initialize the out parameter
166             columnKeyStoreProvider = null;
167
168             // Search in the sytem provider list.
169             if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
170             {
171                 return true;
172             }
173
174             lock (_CustomColumnEncryptionKeyProvidersLock)
175             {
176                 // If custom provider is not set, then return false
177                 if (_CustomColumnEncryptionKeyStoreProviders == null)
178                 {
179                     return false;
180                 }
181
182                 // Search in the custom provider list
183                 return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
184             }
185         }
186
187         /// <summary>
188         /// This function returns a list of system provider dictionary currently supported by this driver.
189         /// </summary>
190         /// <returns>Combined list of provider names</returns>
191         static internal List<string> GetColumnEncryptionSystemKeyStoreProviders() {
192             HashSet<string> providerNames = new HashSet<string>(_SystemColumnEncryptionKeyStoreProviders.Keys);
193             return providerNames.ToList();
194         }
195
196         /// <summary>
197         /// This function returns a list of custom provider dictionary currently registered.
198         /// </summary>
199         /// <returns>Combined list of provider names</returns>
200         static internal List<string> GetColumnEncryptionCustomKeyStoreProviders() {
201             if(_CustomColumnEncryptionKeyStoreProviders != null)
202             {
203                 HashSet<string> providerNames = new HashSet<string>(_CustomColumnEncryptionKeyStoreProviders.Keys);
204                 return providerNames.ToList();
205             }
206
207             return new List<string>();
208         }
209
210         private SqlDebugContext _sdc;   // SQL Debugging support
211
212         private bool    _AsyncCommandInProgress;
213
214         // SQLStatistics support
215         internal SqlStatistics _statistics;
216         private bool _collectstats;
217
218         private bool _fireInfoMessageEventOnUserErrors; // False by default
219
220         // root task associated with current async invocation
221         Tuple<TaskCompletionSource<DbConnectionInternal>, Task> _currentCompletion;
222
223         private SqlCredential _credential; // SQL authentication password stored in SecureString
224         private string _connectionString;
225         private int _connectRetryCount;
226
227         private string _accessToken; // Access Token to be used for token based authententication
228
229         // connection resiliency
230         private object _reconnectLock = new object();
231         internal Task _currentReconnectionTask;
232         private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
233         private Guid _originalConnectionId = Guid.Empty;
234         private CancellationTokenSource _reconnectionCancellationSource;
235         internal SessionData _recoverySessionData;
236         internal WindowsIdentity _lastIdentity;
237         internal WindowsIdentity _impersonateIdentity;
238         private int _reconnectCount;
239
240         // Transient Fault handling flag. This is needed to convey to the downstream mechanism of connection establishment, if Transient Fault handling should be used or not
241         // The downstream handling of Connection open is the same for idle connection resiliency. Currently we want to apply transient fault handling only to the connections opened
242         // using SqlConnection.Open() method. 
243         internal bool _applyTransientFaultHandling = false;
244        
245         public SqlConnection(string connectionString) : this(connectionString, null) {
246         }
247
248         public SqlConnection(string connectionString, SqlCredential credential) : this() {
249             ConnectionString = connectionString;    // setting connection string first so that ConnectionOption is available
250             if (credential != null)
251             {
252                 // The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
253                 //  CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
254                 //  Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
255                 SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
256                 if (UsesClearUserIdOrPassword(connectionOptions))
257                 {
258                     throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
259                 }
260
261                 if (UsesIntegratedSecurity(connectionOptions))
262                 {
263                     throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
264                 }
265
266                 if (UsesContextConnection(connectionOptions))
267                 {
268                     throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
269                 }
270
271                 if (UsesActiveDirectoryIntegrated(connectionOptions))
272                 {
273                      throw SQL.SettingCredentialWithIntegratedArgument();
274                 }
275
276                 Credential = credential;
277             }
278             // else
279             //      credential == null:  we should not set "Credential" as this will do additional validation check and
280             //      checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
281             CacheConnectionStringProperties();
282         }
283
284         private SqlConnection(SqlConnection connection) { // Clone
285             GC.SuppressFinalize(this);
286             CopyFrom(connection);
287             _connectionString = connection._connectionString;
288             if (connection._credential != null)
289             {
290                 SecureString password = connection._credential.Password.Copy();
291                 password.MakeReadOnly();
292                 _credential = new SqlCredential(connection._credential.UserId, password);
293             }
294             _accessToken = connection._accessToken;
295             CacheConnectionStringProperties();
296         }
297
298         // This method will be called once connection string is set or changed. 
299         private void CacheConnectionStringProperties() {
300             SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
301             if (connString != null) {
302                 _connectRetryCount = connString.ConnectRetryCount;
303             }
304         }
305
306         //
307         // PUBLIC PROPERTIES
308         //
309
310         // used to start/stop collection of statistics data and do verify the current state
311         //
312         // devnote: start/stop should not performed using a property since it requires execution of code
313         //
314         // start statistics
315         //  set the internal flag (_statisticsEnabled) to true.
316         //  Create a new SqlStatistics object if not already there.
317         //  connect the parser to the object.
318         //  if there is no parser at this time we need to connect it after creation.
319         //
320
321         [
322         DefaultValue(false),
323         ResCategoryAttribute(Res.DataCategory_Data),
324         ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
325         ]
326         public bool StatisticsEnabled {
327             get {
328                 return (_collectstats);
329             }
330             set {
331                 if (IsContextConnection) {
332                     if (value) {
333                         throw SQL.NotAvailableOnContextConnection();
334                     }
335                 }
336                 else {
337                     if (value) {
338                         // start
339                         if (ConnectionState.Open == State) {
340                             if (null == _statistics) {
341                                 _statistics = new SqlStatistics();
342                                 ADP.TimerCurrent(out _statistics._openTimestamp);
343                             }
344                             // set statistics on the parser
345                             // update timestamp;
346                             Debug.Assert(Parser != null, "Where's the parser?");
347                             Parser.Statistics = _statistics;
348                         }
349                     }
350                     else {
351                         // stop
352                         if (null != _statistics) {
353                             if (ConnectionState.Open == State) {
354                                 // remove statistics from parser
355                                 // update timestamp;
356                                 TdsParser parser = Parser;
357                                 Debug.Assert(parser != null, "Where's the parser?");
358                                 parser.Statistics = null;
359                                 ADP.TimerCurrent(out _statistics._closeTimestamp);
360                             }
361                         }
362                     }
363                     this._collectstats = value;
364                 }
365             }
366         }
367
368         internal bool AsyncCommandInProgress  {
369             get {
370                 return (_AsyncCommandInProgress);
371             }
372             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
373             set {
374                 _AsyncCommandInProgress = value;
375             }
376         }
377
378         internal bool IsContextConnection {
379             get {
380                 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
381                 return UsesContextConnection(opt);
382             }
383         }
384
385         /// <summary>
386         /// Is this connection using column encryption ?
387         /// </summary>
388         internal bool IsColumnEncryptionSettingEnabled {
389             get {
390                 SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
391                 return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
392             }
393         }
394
395         // Is this connection is a Context Connection?
396         private bool UsesContextConnection(SqlConnectionString opt)
397         {
398             return opt != null ? opt.ContextConnection : false;
399         }
400
401         private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt) 
402         {
403              return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
404         }
405
406         private bool UsesAuthentication(SqlConnectionString opt) {
407              return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
408         }
409         
410         // Does this connection uses Integrated Security?
411         private bool UsesIntegratedSecurity(SqlConnectionString opt) {
412             return opt != null ? opt.IntegratedSecurity : false;
413         }
414
415         // Does this connection uses old style of clear userID or Password in connection string?
416         private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
417             bool result = false;
418             if (null != opt) {
419                 result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
420             }
421             return result;
422         }
423
424         internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
425             get {
426                 return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
427             }
428         }
429
430         internal SqlConnectionString.TypeSystem TypeSystem {
431             get {
432                 return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion;
433             }
434         }
435
436         internal Version TypeSystemAssemblyVersion {
437             get {
438                 return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion;
439             }
440         }        
441
442         internal int ConnectRetryInterval {
443             get {
444                 return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval;
445             }
446         }
447
448         override protected DbProviderFactory DbProviderFactory {
449             get {
450                 return SqlClientFactory.Instance;
451             }
452         }
453
454         // AccessToken: To be used for token based authentication
455         [
456         Browsable(false),
457         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
458         ResDescriptionAttribute(Res.SqlConnection_AccessToken),
459         ]
460         public string AccessToken {
461             get {
462                 string result = _accessToken;
463                 // When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
464                 // otherwise, return null
465                 SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
466                 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo) {
467                     result = null;
468                 }
469
470                 return result;
471             }
472             set {
473                 // If a connection is connecting or is ever opened, AccessToken cannot be set
474                 if (!InnerConnection.AllowSetConnectionString) {
475                     throw ADP.OpenConnectionPropertySet("AccessToken", InnerConnection.State);
476                 }
477                 
478                 if (value != null) {
479                     // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
480                     CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
481                 }
482                 
483                 _accessToken = value;
484                 // Need to call ConnectionString_Set to do proper pool group check
485                 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: _accessToken));
486             }
487         }
488
489         [
490         DefaultValue(""),
491 #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
492         RecommendedAsConfigurable(true),
493 #pragma warning restore 618
494         SettingsBindableAttribute(true),
495         RefreshProperties(RefreshProperties.All),
496         ResCategoryAttribute(Res.DataCategory_Data),
497         Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
498         ResDescriptionAttribute(Res.SqlConnection_ConnectionString),
499         ]
500         override public string ConnectionString {
501             get {
502                 return ConnectionString_Get();
503             }
504             set {
505                 if(_credential != null || _accessToken != null) {
506                     SqlConnectionString connectionOptions = new SqlConnectionString(value);
507                     if(_credential != null) {
508                         // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
509                         // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
510                         // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
511                         if(UsesActiveDirectoryIntegrated(connectionOptions)) {
512                             throw SQL.SettingIntegratedWithCredential();
513                         }
514
515                         CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
516                     }
517                     else if(_accessToken != null) {
518                         CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
519                     }
520                 }
521                 ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
522                 _connectionString = value;  // Change _connectionString value only after value is validated
523                 CacheConnectionStringProperties();
524             }
525         }
526
527         [
528         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
529         ResDescriptionAttribute(Res.SqlConnection_ConnectionTimeout),
530         ]
531         override public int ConnectionTimeout {
532             get {
533                 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
534                 return ((null != constr) ? constr.ConnectTimeout : SqlConnectionString.DEFAULT.Connect_Timeout);
535             }
536         }
537
538         [
539         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
540         ResDescriptionAttribute(Res.SqlConnection_Database),
541         ]
542         override public string Database {
543             // if the connection is open, we need to ask the inner connection what it's
544             // current catalog is because it may have gotten changed, otherwise we can
545             // just return what the connection string had.
546             get {
547                 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
548                 string result;
549
550                 if (null != innerConnection) {
551                     result = innerConnection.CurrentDatabase;
552                 }
553                 else {
554                     SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
555                     result = ((null != constr) ? constr.InitialCatalog : SqlConnectionString.DEFAULT.Initial_Catalog);
556                 }
557                 return result;
558             }
559         }
560
561         [
562         Browsable(true),
563         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
564         ResDescriptionAttribute(Res.SqlConnection_DataSource),
565         ]
566         override public string DataSource {
567             get {
568                 SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
569                 string result;
570
571                 if (null != innerConnection) {
572                     result = innerConnection.CurrentDataSource;
573                 }
574                 else {
575                     SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
576                     result = ((null != constr) ? constr.DataSource : SqlConnectionString.DEFAULT.Data_Source);
577                 }
578                 return result;
579             }
580         }
581
582         [
583         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
584         ResCategoryAttribute(Res.DataCategory_Data),
585         ResDescriptionAttribute(Res.SqlConnection_PacketSize),
586         ]
587         public int PacketSize {
588             // if the connection is open, we need to ask the inner connection what it's
589             // current packet size is because it may have gotten changed, otherwise we
590             // can just return what the connection string had.
591             get {
592                 if (IsContextConnection) {
593                     throw SQL.NotAvailableOnContextConnection();
594                 }
595
596                 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
597                 int result;
598
599                 if (null != innerConnection) {
600                     result = innerConnection.PacketSize;
601                 }
602                 else {
603                     SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
604                     result = ((null != constr) ? constr.PacketSize : SqlConnectionString.DEFAULT.Packet_Size);
605                 }
606                 return result;
607             }
608         }
609
610         [
611         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
612         ResCategoryAttribute(Res.DataCategory_Data),
613         ResDescriptionAttribute(Res.SqlConnection_ClientConnectionId),
614         ]
615         public Guid ClientConnectionId {
616             get {
617
618                 SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
619
620                 if (null != innerConnection) {
621                     return innerConnection.ClientConnectionId;
622                 }
623                 else {
624                     Task reconnectTask = _currentReconnectionTask;
625                     if (reconnectTask != null && !reconnectTask.IsCompleted) {
626                         return _originalConnectionId;
627                     }
628                     return Guid.Empty;
629                 }
630             }
631         }
632
633         [
634         Browsable(false),
635         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
636         ResDescriptionAttribute(Res.SqlConnection_ServerVersion),
637         ]
638         override public string ServerVersion {
639             get {
640                 return GetOpenConnection().ServerVersion;
641             }
642         }
643
644         [
645         Browsable(false),
646         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
647         ResDescriptionAttribute(Res.DbConnection_State),
648         ]
649         override public ConnectionState State {
650             get {
651                 Task reconnectTask=_currentReconnectionTask;
652                 if (reconnectTask != null && !reconnectTask.IsCompleted) {
653                     return ConnectionState.Open;
654                 }
655                 return InnerConnection.State;
656             }
657         }
658
659
660         internal SqlStatistics Statistics {
661             get {
662                 return _statistics;
663             }
664         }
665
666         [
667         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
668         ResCategoryAttribute(Res.DataCategory_Data),
669         ResDescriptionAttribute(Res.SqlConnection_WorkstationId),
670         ]
671         public string WorkstationId {
672             get {
673                 if (IsContextConnection) {
674                     throw SQL.NotAvailableOnContextConnection();
675                 }
676
677                 // If not supplied by the user, the default value is the MachineName
678                 // Note: In Longhorn you'll be able to rename a machine without
679                 // rebooting.  Therefore, don't cache this machine name.
680                 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
681                 string result = ((null != constr) ? constr.WorkstationId : null);
682                 if (null == result) {
683                     // getting machine name requires Environment.Permission
684                     // user must have that permission in order to retrieve this
685                     result = Environment.MachineName;
686                 }
687                 return result;
688             }
689         }
690
691         // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
692         [
693         Browsable(false),
694         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
695         ResDescriptionAttribute(Res.SqlConnection_Credential),
696         ]
697         public SqlCredential Credential
698         {
699             get
700             {
701                 SqlCredential result = _credential;
702
703                 // When a connection is connecting or is ever opened, make credential available only if "Persist Security Info" is set to true
704                 //  otherwise, return null
705                 SqlConnectionString connectionOptions = (SqlConnectionString) UserConnectionOptions;
706                 if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo)
707                 {
708                     result = null;
709                 }
710
711                 return result;
712             }
713
714             set
715             {
716                 // If a connection is connecting or is ever opened, user id/password cannot be set
717                 if (!InnerConnection.AllowSetConnectionString)
718                 {
719                     throw ADP.OpenConnectionPropertySet("Credential", InnerConnection.State);
720                 }
721
722                 // check if the usage of credential has any conflict with the keys used in connection string
723                 if (value != null)
724                 {
725                     // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
726                     // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
727                     // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
728                     if (UsesActiveDirectoryIntegrated((SqlConnectionString) ConnectionOptions)) {
729                         throw SQL.SettingCredentialWithIntegratedInvalid();
730                     }
731
732                     CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString) ConnectionOptions);
733                     if(_accessToken != null) {
734                         throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
735                     }
736
737                 }
738                 
739                 _credential = value;
740
741                 // Need to call ConnectionString_Set to do proper pool group check
742                 ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
743             }
744         }
745
746         // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential: check if the usage of credential has any conflict
747         //  with the keys used in connection string
748         //  If there is any conflict, it throws InvalidOperationException
749         //  This is to be used setter of ConnectionString and Credential properties
750         private void CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(SqlConnectionString connectionOptions)
751         {
752             if (UsesClearUserIdOrPassword(connectionOptions))
753             {
754                 throw ADP.InvalidMixedUsageOfSecureAndClearCredential();
755             }
756
757             if (UsesIntegratedSecurity(connectionOptions))
758             {
759                 throw ADP.InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity();
760             }
761
762             if (UsesContextConnection(connectionOptions))
763             {
764                 throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
765             }
766         }
767
768         // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
769         //  with the keys used in connection string and credential
770         //  If there is any conflict, it throws InvalidOperationException
771         //  This is to be used setter of ConnectionString and AccessToken properties
772         private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions) {
773             if (UsesClearUserIdOrPassword(connectionOptions)) {
774                 throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
775             }
776
777             if (UsesIntegratedSecurity(connectionOptions)) {
778                 throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
779             }
780
781             if (UsesContextConnection(connectionOptions)) {
782                 throw ADP.InvalidMixedUsageOfAccessTokenAndContextConnection();
783             }
784
785             if (UsesAuthentication(connectionOptions)) {
786                 throw ADP.InvalidMixedUsageOfAccessTokenAndAuthentication();
787             }
788
789             // Check if the usage of AccessToken has the conflict with credential
790             if (_credential != null) {
791                 throw ADP.InvalidMixedUsageOfAccessTokenAndCredential();
792             }
793         }
794
795         //
796         // PUBLIC EVENTS
797         //
798
799         [
800         ResCategoryAttribute(Res.DataCategory_InfoMessage),
801         ResDescriptionAttribute(Res.DbConnection_InfoMessage),
802         ]
803         public event SqlInfoMessageEventHandler InfoMessage {
804             add {
805                 Events.AddHandler(EventInfoMessage, value);
806             }
807             remove {
808                 Events.RemoveHandler(EventInfoMessage, value);
809             }
810         }
811
812         public bool FireInfoMessageEventOnUserErrors {
813             get {
814                 return _fireInfoMessageEventOnUserErrors;
815             }
816             set {
817                 _fireInfoMessageEventOnUserErrors = value;
818             }
819         }
820
821         // Approx. number of times that the internal connection has been reconnected
822         internal int ReconnectCount {
823             get {
824                 return _reconnectCount;
825             }
826         }
827
828         //
829         // PUBLIC METHODS
830         //
831
832         new public SqlTransaction BeginTransaction() {
833             // this is just a delegate. The actual method tracks executiontime
834             return BeginTransaction(IsolationLevel.Unspecified, null);
835         }
836
837         new public SqlTransaction BeginTransaction(IsolationLevel iso) {
838             // this is just a delegate. The actual method tracks executiontime
839             return BeginTransaction(iso, null);
840         }
841
842         public SqlTransaction BeginTransaction(string transactionName) {
843                 // Use transaction names only on the outermost pair of nested
844                 // BEGIN...COMMIT or BEGIN...ROLLBACK statements.  Transaction names
845                 // are ignored for nested BEGIN's.  The only way to rollback a nested
846                 // transaction is to have a save point from a SAVE TRANSACTION call.
847                 return BeginTransaction(IsolationLevel.Unspecified, transactionName);
848         }
849
850         // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
851         [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
852         override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
853             IntPtr hscp;
854
855             Bid.ScopeEnter(out hscp, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
856             try {
857
858                 DbTransaction transaction = BeginTransaction(isolationLevel);
859
860                 // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and 
861                 //   subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction
862                 //   is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
863                 //   until the completion of BeginTransaction with KeepAlive
864                 GC.KeepAlive(this);
865
866                 return transaction;
867             }
868             finally {
869                 Bid.ScopeLeave(ref hscp);
870             }
871         }
872
873         public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) {
874             WaitForPendingReconnection();
875             SqlStatistics statistics = null;
876             IntPtr hscp;
877             string xactName =  ADP.IsEmpty(transactionName)? "None" : transactionName;
878             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", ObjectID, (int)iso,
879                         xactName);
880
881             try {
882                 statistics = SqlStatistics.StartTimer(Statistics);
883
884                 // NOTE: we used to throw an exception if the transaction name was empty
885                 // (see MDAC 50292) but that was incorrect because we have a BeginTransaction
886                 // method that doesn't have a transactionName argument.
887                 SqlTransaction transaction;
888                 bool isFirstAttempt = true;
889                 do {
890                     transaction = GetOpenConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice
891                     Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt");
892                     isFirstAttempt = false;
893                 } while (transaction.InternalTransaction.ConnectionHasBeenRestored);
894
895
896                 // SQLBU 503873  The GetOpenConnection line above doesn't keep a ref on the outer connection (this),
897                 //  and it could be collected before the inner connection can hook it to the transaction, resulting in
898                 //  a transaction with a null connection property.  Use GC.KeepAlive to ensure this doesn't happen.
899                 GC.KeepAlive(this);
900
901                 return transaction;
902             }
903             finally {
904                 Bid.ScopeLeave(ref hscp);
905                 SqlStatistics.StopTimer(statistics);
906             }
907         }
908
909         override public void ChangeDatabase(string database) {
910             SqlStatistics statistics = null;
911             RepairInnerConnection();
912             Bid.CorrelationTrace("<sc.SqlConnection.ChangeDatabase|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
913             TdsParser bestEffortCleanupTarget = null;
914             RuntimeHelpers.PrepareConstrainedRegions();
915             try {
916 #if DEBUG
917                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
918
919                 RuntimeHelpers.PrepareConstrainedRegions();
920                 try {
921                     tdsReliabilitySection.Start();
922 #else
923                 {
924 #endif //DEBUG
925                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
926                     statistics = SqlStatistics.StartTimer(Statistics);
927                     InnerConnection.ChangeDatabase(database);
928                 }
929 #if DEBUG
930                 finally {
931                     tdsReliabilitySection.Stop();
932                 }
933 #endif //DEBUG
934             }
935             catch (System.OutOfMemoryException e) {
936                 Abort(e);
937                 throw;
938             }
939             catch (System.StackOverflowException e) {
940                 Abort(e);
941                 throw;
942             }
943             catch (System.Threading.ThreadAbortException e) {
944                 Abort(e);
945                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
946                 throw;
947             }
948             finally {
949                 SqlStatistics.StopTimer(statistics);
950             }
951         }
952
953         static public void ClearAllPools() {
954             (new SqlClientPermission(PermissionState.Unrestricted)).Demand();
955             SqlConnectionFactory.SingletonInstance.ClearAllPools();
956         }
957
958         static public void ClearPool(SqlConnection connection) {
959             ADP.CheckArgumentNull(connection, "connection");
960
961             DbConnectionOptions connectionOptions = connection.UserConnectionOptions;
962             if (null != connectionOptions) {
963                 connectionOptions.DemandPermission();
964                 if (connection.IsContextConnection) {
965                     throw SQL.NotAvailableOnContextConnection();
966                 }
967                 SqlConnectionFactory.SingletonInstance.ClearPool(connection);
968             }
969         }
970
971         object ICloneable.Clone() {
972             SqlConnection clone = new SqlConnection(this);
973             Bid.Trace("<sc.SqlConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
974             return clone;
975         }
976
977         void CloseInnerConnection() {
978             // CloseConnection() now handles the lock
979
980             // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and 
981             // the command will no longer be cancelable.  It might be desirable to be able to cancel the close opperation, but this is
982             // outside of the scope of Whidbey RTM.  See (SqlCommand::Cancel) for other lock.
983             InnerConnection.CloseConnection(this, ConnectionFactory);
984         }
985
986         override public void Close() {
987             IntPtr hscp;
988             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
989             Bid.CorrelationTrace("<sc.SqlConnection.Close|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
990             try {
991                 SqlStatistics statistics = null;
992
993                 TdsParser bestEffortCleanupTarget = null;
994                 RuntimeHelpers.PrepareConstrainedRegions();
995                 try {
996 #if DEBUG
997                     TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
998
999                     RuntimeHelpers.PrepareConstrainedRegions();
1000                     try {
1001                         tdsReliabilitySection.Start();
1002 #else
1003                     {
1004 #endif //DEBUG
1005                         bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1006                         statistics = SqlStatistics.StartTimer(Statistics);
1007
1008                         Task reconnectTask = _currentReconnectionTask;
1009                         if (reconnectTask != null && !reconnectTask.IsCompleted) {
1010                             CancellationTokenSource cts = _reconnectionCancellationSource;
1011                             if (cts != null) {
1012                                 cts.Cancel();
1013                             }
1014                             AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection
1015                             if (State != ConnectionState.Open) {// if we cancelled before the connection was opened 
1016                                 OnStateChange(DbConnectionInternal.StateChangeClosed);
1017                             }
1018                         }
1019                         CancelOpenAndWait(); 
1020                         CloseInnerConnection();
1021                         GC.SuppressFinalize(this);
1022
1023                         if (null != Statistics) {
1024                             ADP.TimerCurrent(out _statistics._closeTimestamp);
1025                         }
1026                     }
1027  #if DEBUG
1028                     finally {
1029                         tdsReliabilitySection.Stop();
1030                     }
1031 #endif //DEBUG
1032                 }
1033                 catch (System.OutOfMemoryException e) {
1034                     Abort(e);
1035                     throw;
1036                 }
1037                 catch (System.StackOverflowException e) {
1038                     Abort(e);
1039                     throw;
1040                 }
1041                 catch (System.Threading.ThreadAbortException e) {
1042                     Abort(e);
1043                     SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1044                     throw;
1045                 }
1046                 finally {
1047                     SqlStatistics.StopTimer(statistics);
1048                 }
1049             }
1050             finally {
1051                 SqlDebugContext  sdc = _sdc;
1052                 _sdc = null;
1053                 Bid.ScopeLeave(ref hscp);
1054                 if (sdc != null) {
1055                    sdc.Dispose();
1056                 }
1057             }
1058         }
1059
1060         new public SqlCommand CreateCommand() {
1061             return new SqlCommand(null, this);
1062         }
1063
1064         private void DisposeMe(bool disposing) { // MDAC 65459
1065             // clear credential and AccessToken here rather than in IDisposable.Dispose as these are specific to SqlConnection only
1066             //  IDisposable.Dispose is generated code from a template and used by other providers as well
1067             _credential = null; 
1068             _accessToken = null;
1069
1070             if (!disposing) {
1071                 // DevDiv2 Bug 457934:SQLConnection leaks when not disposed
1072                 // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/457934
1073                 // For non-pooled connections we need to make sure that if the SqlConnection was not closed, then we release the GCHandle on the stateObject to allow it to be GCed
1074                 // For pooled connections, we will rely on the pool reclaiming the connection
1075                 var innerConnection = (InnerConnection as SqlInternalConnectionTds);
1076                 if ((innerConnection != null) && (!innerConnection.ConnectionOptions.Pooling)) {
1077                     var parser = innerConnection.Parser;
1078                     if ((parser != null) && (parser._physicalStateObj != null)) {
1079                         parser._physicalStateObj.DecrementPendingCallbacks(release: false);
1080                     }
1081                 }
1082             }
1083         }
1084
1085 #if !MOBILE
1086         public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) {
1087             if (IsContextConnection) {
1088                 throw SQL.NotAvailableOnContextConnection();
1089             }
1090
1091             EnlistDistributedTransactionHelper(transaction);
1092         }
1093 #endif
1094
1095         override public void Open() {
1096             IntPtr hscp;
1097             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Open|API> %d#", ObjectID) ;
1098             Bid.CorrelationTrace("<sc.SqlConnection.Open|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1099            
1100             try {
1101                 if (StatisticsEnabled) {
1102                     if (null == _statistics) {
1103                         _statistics = new SqlStatistics();
1104                     }
1105                     else {
1106                         _statistics.ContinueOnNewConnection();
1107                     }
1108                 }
1109
1110                 SqlStatistics statistics = null;
1111                 RuntimeHelpers.PrepareConstrainedRegions();
1112                 try {
1113                     statistics = SqlStatistics.StartTimer(Statistics);
1114
1115                     if (!TryOpen(null)) {
1116                         throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending);
1117                     }
1118                 }
1119                 finally {
1120                     SqlStatistics.StopTimer(statistics);
1121                 }
1122             }
1123             finally {
1124                 Bid.ScopeLeave(ref hscp);
1125             }
1126         }
1127
1128         internal void RegisterWaitingForReconnect(Task waitingTask) {
1129             if (((SqlConnectionString)ConnectionOptions).MARS) {
1130                 return;
1131             }
1132             Interlocked.CompareExchange(ref _asyncWaitingForReconnection, waitingTask, null);
1133             if (_asyncWaitingForReconnection != waitingTask) { // somebody else managed to register 
1134                 throw SQL.MARSUnspportedOnConnection();
1135             }
1136         }
1137
1138         private async Task ReconnectAsync(int timeout) {
1139             try {
1140                 long commandTimeoutExpiration = 0;
1141                 if (timeout > 0) {
1142                     commandTimeoutExpiration = ADP.TimerCurrent() + ADP.TimerFromSeconds(timeout);
1143                 }
1144                 CancellationTokenSource cts = new CancellationTokenSource();
1145                 _reconnectionCancellationSource = cts;
1146                 CancellationToken ctoken = cts.Token;
1147                 int retryCount = _connectRetryCount; // take a snapshot: could be changed by modifying the connection string
1148                 for (int attempt = 0; attempt < retryCount; attempt++) {                                       
1149                     if (ctoken.IsCancellationRequested) {
1150                         Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - reconnection cancelled\n", _originalConnectionId.ToString());
1151                         return;
1152                     }
1153                     try {
1154                         _impersonateIdentity = _lastIdentity;
1155                         try {
1156                             ForceNewConnection = true;
1157                             await OpenAsync(ctoken).ConfigureAwait(false);
1158                             // On success, increment the reconnect count - we don't really care if it rolls over since it is approx.
1159                             _reconnectCount = unchecked(_reconnectCount + 1);
1160 #if DEBUG
1161                             Debug.Assert(_recoverySessionData._debugReconnectDataApplied, "Reconnect data was not applied !");
1162 #endif
1163                         }
1164                         finally {
1165                             _impersonateIdentity = null;
1166                             ForceNewConnection = false;
1167                         }
1168                         Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Reconnection suceeded.  ClientConnectionID %ls -> %ls \n", _originalConnectionId.ToString(), ClientConnectionId.ToString());
1169                         return;
1170                     }
1171                     catch (SqlException e) {
1172                         Bid.Trace("<sc.SqlConnection.ReconnectAsyncINFO> Orginal ClientConnectionID %ls - reconnection attempt failed error %ls\n", _originalConnectionId.ToString(), e.Message);
1173                         if (attempt == retryCount - 1) {
1174                             Bid.Trace("<sc.SqlConnection.ReconnectAsync|INFO> Orginal ClientConnectionID %ls - give up reconnection\n", _originalConnectionId.ToString());
1175                             throw SQL.CR_AllAttemptsFailed(e, _originalConnectionId);
1176                         }
1177                         if (timeout > 0 && ADP.TimerRemaining(commandTimeoutExpiration) < ADP.TimerFromSeconds(ConnectRetryInterval)) {
1178                             throw SQL.CR_NextAttemptWillExceedQueryTimeout(e, _originalConnectionId);
1179                         }
1180                     }
1181                     await Task.Delay(1000 * ConnectRetryInterval, ctoken).ConfigureAwait(false);
1182                 }
1183             }
1184             finally {               
1185                 _recoverySessionData = null;
1186                 _supressStateChangeForReconnection = false;
1187             }
1188             Debug.Assert(false, "Should not reach this point");
1189         }
1190
1191         internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) {
1192             Task runningReconnect = _currentReconnectionTask;
1193             // This loop in the end will return not completed reconnect task or null
1194             while (runningReconnect != null && runningReconnect.IsCompleted) {
1195                 // clean current reconnect task (if it is the same one we checked
1196                 Interlocked.CompareExchange<Task>(ref _currentReconnectionTask, null, runningReconnect);
1197                 // make sure nobody started new task (if which case we did not clean it)
1198                 runningReconnect = _currentReconnectionTask;
1199             }
1200             if (runningReconnect == null) {
1201                 if (_connectRetryCount > 0) {
1202                     SqlInternalConnectionTds tdsConn = GetOpenTdsConnection();                    
1203                     if (tdsConn._sessionRecoveryAcknowledged) {
1204                         TdsParserStateObject stateObj = tdsConn.Parser._physicalStateObj;     
1205                         if (!stateObj.ValidateSNIConnection()) {                           
1206                             if (tdsConn.Parser._sessionPool != null) {
1207                                 if (tdsConn.Parser._sessionPool.ActiveSessionsCount > 0) {
1208                                     // >1 MARS session 
1209                                     if (beforeDisconnect != null) {
1210                                         beforeDisconnect();
1211                                     }
1212                                     OnError(SQL.CR_UnrecoverableClient(ClientConnectionId), true, null);
1213                                 }
1214                             }
1215                             SessionData cData = tdsConn.CurrentSessionData;
1216                             cData.AssertUnrecoverableStateCountIsCorrect();
1217                             if (cData._unrecoverableStatesCount == 0) {
1218                                 bool callDisconnect = false;
1219                                 lock (_reconnectLock) {
1220                                     tdsConn.CheckEnlistedTransactionBinding();
1221                                     runningReconnect = _currentReconnectionTask; // double check after obtaining the lock
1222                                     if (runningReconnect == null) {
1223                                         if (cData._unrecoverableStatesCount == 0) { // could change since the first check, but now is stable since connection is know to be broken
1224                                             _originalConnectionId = ClientConnectionId;
1225                                             Bid.Trace("<sc.SqlConnection.ReconnectIfNeeded|INFO> Connection ClientConnectionID %ls is invalid, reconnecting\n", _originalConnectionId.ToString());
1226                                             _recoverySessionData = cData;
1227                                             if (beforeDisconnect != null) {
1228                                                 beforeDisconnect();
1229                                             }
1230                                             try {
1231                                                 _supressStateChangeForReconnection = true;
1232                                                 tdsConn.DoomThisConnection();
1233                                             }
1234                                             catch (SqlException) {
1235                                             }
1236                                             runningReconnect = Task.Run(() => ReconnectAsync(timeout));
1237                                             // if current reconnect is not null, somebody already started reconnection task - some kind of race condition
1238                                             Debug.Assert(_currentReconnectionTask == null, "Duplicate reconnection tasks detected");                                            
1239                                             _currentReconnectionTask = runningReconnect;
1240                                         }
1241                                     }
1242                                     else {
1243                                         callDisconnect = true;
1244                                     }
1245                                 }
1246                                 if (callDisconnect && beforeDisconnect != null) {
1247                                     beforeDisconnect();
1248                                 }
1249                             }
1250                             else {
1251                                 if (beforeDisconnect != null) {
1252                                     beforeDisconnect();
1253                                 }
1254                                 OnError(SQL.CR_UnrecoverableServer(ClientConnectionId), true, null);
1255                             }
1256                         } // ValidateSNIConnection
1257                     } // sessionRecoverySupported                  
1258                 } // connectRetryCount>0
1259             }
1260             else { // runningReconnect = null
1261                 if (beforeDisconnect != null) {
1262                     beforeDisconnect();
1263                 }
1264             }
1265             return runningReconnect;
1266         }
1267
1268         // this is straightforward, but expensive method to do connection resiliency - it take locks and all prepartions as for TDS request
1269         partial void RepairInnerConnection() {
1270             WaitForPendingReconnection();
1271             if (_connectRetryCount == 0) {
1272                 return;
1273             }
1274             SqlInternalConnectionTds tdsConn = InnerConnection as SqlInternalConnectionTds;
1275             if (tdsConn != null) {
1276                 tdsConn.ValidateConnectionForExecute(null);
1277                 tdsConn.GetSessionAndReconnectIfNeeded((SqlConnection)this);
1278             }
1279         }
1280
1281         private void WaitForPendingReconnection() {
1282             Task reconnectTask = _currentReconnectionTask;
1283             if (reconnectTask != null && !reconnectTask.IsCompleted) {
1284                 AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false);
1285             }
1286         }
1287
1288         void CancelOpenAndWait()
1289         {
1290             // copy from member to avoid changes by background thread
1291             var completion = _currentCompletion;
1292             if (completion != null)
1293             {
1294                 completion.Item1.TrySetCanceled();
1295                 ((IAsyncResult)completion.Item2).AsyncWaitHandle.WaitOne();
1296             }
1297             Debug.Assert(_currentCompletion == null, "After waiting for an async call to complete, there should be no completion source");
1298         }
1299
1300         public override Task OpenAsync(CancellationToken cancellationToken) {
1301             IntPtr hscp;
1302             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.OpenAsync|API> %d#", ObjectID) ;
1303             Bid.CorrelationTrace("<sc.SqlConnection.OpenAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
1304             try {
1305
1306                 if (StatisticsEnabled) {
1307                     if (null == _statistics) {
1308                         _statistics = new SqlStatistics();
1309                     }
1310                     else {
1311                         _statistics.ContinueOnNewConnection();
1312                     }
1313                 }
1314
1315                 SqlStatistics statistics = null;
1316                 RuntimeHelpers.PrepareConstrainedRegions();
1317                 try {
1318                     statistics = SqlStatistics.StartTimer(Statistics);
1319
1320                     System.Transactions.Transaction transaction = ADP.GetCurrentTransaction();
1321                     TaskCompletionSource<DbConnectionInternal> completion = new TaskCompletionSource<DbConnectionInternal>(transaction);
1322                     TaskCompletionSource<object> result = new TaskCompletionSource<object>();
1323
1324                     if (cancellationToken.IsCancellationRequested) {
1325                         result.SetCanceled();
1326                         return result.Task;
1327                     }
1328
1329                     if (IsContextConnection) {
1330                         // Async not supported on Context Connections
1331                         result.SetException(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
1332                         return result.Task;
1333                     }
1334
1335                     bool completed;
1336                     
1337                     try {
1338                         completed = TryOpen(completion);
1339                     }
1340                     catch (Exception e) {
1341                         result.SetException(e);
1342                         return result.Task;
1343                     }
1344                     
1345                     if (completed) {
1346                         result.SetResult(null);
1347                     }
1348                     else {
1349                         CancellationTokenRegistration registration = new CancellationTokenRegistration();
1350                         if (cancellationToken.CanBeCanceled) {
1351                             registration = cancellationToken.Register(() => completion.TrySetCanceled());
1352                         }
1353                         OpenAsyncRetry retry = new OpenAsyncRetry(this, completion, result, registration);
1354                         _currentCompletion = new Tuple<TaskCompletionSource<DbConnectionInternal>, Task>(completion, result.Task);
1355                         completion.Task.ContinueWith(retry.Retry, TaskScheduler.Default);
1356                         return result.Task;
1357                     }
1358
1359                     return result.Task;
1360                 }
1361                 finally {
1362                     SqlStatistics.StopTimer(statistics);
1363                 }
1364             }
1365             finally {
1366                 Bid.ScopeLeave(ref hscp);
1367             }
1368         }
1369
1370         private class OpenAsyncRetry {
1371             SqlConnection _parent;
1372             TaskCompletionSource<DbConnectionInternal> _retry;
1373             TaskCompletionSource<object> _result;
1374             CancellationTokenRegistration _registration;
1375
1376             public OpenAsyncRetry(SqlConnection parent, TaskCompletionSource<DbConnectionInternal> retry, TaskCompletionSource<object> result,  CancellationTokenRegistration registration) {
1377                 _parent = parent;
1378                 _retry = retry;
1379                 _result = result;
1380                 _registration = registration;
1381             }
1382
1383             internal void Retry(Task<DbConnectionInternal> retryTask) {
1384                 Bid.Trace("<sc.SqlConnection.OpenAsyncRetry|Info> %d#\n", _parent.ObjectID);
1385                 _registration.Dispose();
1386                 try {
1387                     SqlStatistics statistics = null;
1388                     RuntimeHelpers.PrepareConstrainedRegions();
1389                     try {
1390                         statistics = SqlStatistics.StartTimer(_parent.Statistics);
1391
1392                         if (retryTask.IsFaulted) {
1393                             Exception e = retryTask.Exception.InnerException;
1394                             _parent.CloseInnerConnection();
1395                             _parent._currentCompletion = null;
1396                             _result.SetException(retryTask.Exception.InnerException);
1397                         }
1398                         else if (retryTask.IsCanceled) {
1399                             _parent.CloseInnerConnection();
1400                             _parent._currentCompletion = null;
1401                             _result.SetCanceled();
1402                         }
1403                         else {
1404                             bool result;
1405                             // protect continuation from ----s with close and cancel
1406                             lock (_parent.InnerConnection) {
1407                                 result = _parent.TryOpen(_retry);
1408                             }
1409                             if (result)
1410                             {
1411                                 _parent._currentCompletion = null;
1412                                 _result.SetResult(null);
1413                             }
1414                             else {
1415                                 _parent.CloseInnerConnection();
1416                                 _parent._currentCompletion = null;
1417                                 _result.SetException(ADP.ExceptionWithStackTrace(ADP.InternalError(ADP.InternalErrorCode.CompletedConnectReturnedPending)));
1418                             }
1419                         }
1420                     }
1421                     finally {
1422                         SqlStatistics.StopTimer(statistics);
1423                     }
1424                 }
1425                 catch (Exception e) {
1426                     _parent.CloseInnerConnection();
1427                     _parent._currentCompletion = null;
1428                     _result.SetException(e);
1429                 }
1430             }
1431         }
1432
1433         private bool TryOpen(TaskCompletionSource<DbConnectionInternal> retry) {
1434             SqlConnectionString connectionOptions = (SqlConnectionString)ConnectionOptions;
1435             
1436             _applyTransientFaultHandling = (retry == null && connectionOptions != null && connectionOptions.ConnectRetryCount > 0 );
1437
1438             if (connectionOptions != null &&
1439                 (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword) &&
1440                 (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) &&
1441                 _credential == null) {
1442                     throw SQL.CredentialsNotProvided(connectionOptions.Authentication);
1443             }
1444
1445            if (_impersonateIdentity != null) {
1446                 if (_impersonateIdentity.User == DbConnectionPoolIdentity.GetCurrentWindowsIdentity().User) {
1447                     return TryOpenInner(retry);
1448                 }
1449                 else {
1450                     using (WindowsImpersonationContext context = _impersonateIdentity.Impersonate()) {
1451                         return TryOpenInner(retry);
1452                     }                    
1453                 }
1454             }
1455             else {
1456                 if (this.UsesIntegratedSecurity(connectionOptions) || this.UsesActiveDirectoryIntegrated(connectionOptions)) {
1457                     _lastIdentity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity();
1458                 }
1459                 else {
1460                     _lastIdentity = null;
1461                 }
1462                 return TryOpenInner(retry);
1463             }
1464         }
1465
1466         private bool TryOpenInner(TaskCompletionSource<DbConnectionInternal> retry) {
1467             TdsParser bestEffortCleanupTarget = null;
1468             RuntimeHelpers.PrepareConstrainedRegions();
1469             try {
1470 #if DEBUG
1471                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1472
1473                 RuntimeHelpers.PrepareConstrainedRegions();
1474                 try {
1475                     tdsReliabilitySection.Start();
1476 #else
1477                 {
1478 #endif //DEBUG
1479                     if (ForceNewConnection) {
1480                         if (!InnerConnection.TryReplaceConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1481                             return false;
1482                         }
1483                     }
1484                     else {
1485                         if (!InnerConnection.TryOpenConnection(this, ConnectionFactory, retry, UserConnectionOptions)) {
1486                             return false;
1487                         }
1488                     }
1489                     // does not require GC.KeepAlive(this) because of OnStateChange
1490
1491                     // GetBestEffortCleanup must happen AFTER OpenConnection to get the correct target.
1492                     bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
1493
1494                     var tdsInnerConnection = (InnerConnection as SqlInternalConnectionTds);
1495                     if (tdsInnerConnection == null) {
1496                         SqlInternalConnectionSmi innerConnection = (InnerConnection as SqlInternalConnectionSmi);
1497                         innerConnection.AutomaticEnlistment();
1498                     }
1499                     else {
1500                         Debug.Assert(tdsInnerConnection.Parser != null, "Where's the parser?");
1501
1502                         if (!tdsInnerConnection.ConnectionOptions.Pooling) {
1503                             // For non-pooled connections, we need to make sure that the finalizer does actually run to avoid leaking SNI handles
1504                             GC.ReRegisterForFinalize(this);
1505                         }
1506
1507                         if (StatisticsEnabled) {
1508                             ADP.TimerCurrent(out _statistics._openTimestamp);
1509                             tdsInnerConnection.Parser.Statistics = _statistics;
1510                         }
1511                         else {
1512                             tdsInnerConnection.Parser.Statistics = null;
1513                             _statistics = null; // in case of previous Open/Close/reset_CollectStats sequence
1514                         }
1515                         CompleteOpen();
1516                     }
1517                 }
1518 #if DEBUG
1519                 finally {
1520                     tdsReliabilitySection.Stop();
1521                 }
1522 #endif //DEBUG
1523             }
1524             catch (System.OutOfMemoryException e) {
1525                 Abort(e);
1526                 throw;
1527             }
1528             catch (System.StackOverflowException e) {
1529                 Abort(e);
1530                 throw;
1531             }
1532             catch (System.Threading.ThreadAbortException e) {
1533                 Abort(e);
1534                 SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
1535                 throw;
1536             }
1537
1538             return true;
1539         }
1540
1541
1542         //
1543         // INTERNAL PROPERTIES
1544         //
1545
1546         internal bool HasLocalTransaction {
1547             get {
1548                 return GetOpenConnection().HasLocalTransaction;
1549             }
1550         }
1551
1552         internal bool HasLocalTransactionFromAPI {
1553             get {
1554                 Task reconnectTask = _currentReconnectionTask;
1555                 if (reconnectTask != null  && !reconnectTask.IsCompleted) {
1556                     return false; //we will not go into reconnection if we are inside the transaction
1557                 }
1558                 return GetOpenConnection().HasLocalTransactionFromAPI;
1559             }
1560         }
1561
1562         internal bool IsShiloh {
1563             get {
1564                 if (_currentReconnectionTask != null) { // holds true even if task is completed
1565                     return true; // if CR is enabled, connection, if established, will be Katmai+
1566                 }
1567                 return GetOpenConnection().IsShiloh;
1568             }
1569         }
1570
1571         internal bool IsYukonOrNewer {
1572             get {
1573                 if (_currentReconnectionTask != null) { // holds true even if task is completed
1574                     return true; // if CR is enabled, connection, if established, will be Katmai+
1575                 }
1576                 return GetOpenConnection().IsYukonOrNewer;
1577             }
1578         }
1579
1580         internal bool IsKatmaiOrNewer {
1581             get {
1582                 if (_currentReconnectionTask != null) { // holds true even if task is completed
1583                     return true; // if CR is enabled, connection, if established, will be Katmai+
1584                 }
1585                 return GetOpenConnection().IsKatmaiOrNewer;
1586             }
1587         }
1588
1589         internal TdsParser Parser {
1590             get {
1591                 SqlInternalConnectionTds tdsConnection = (GetOpenConnection() as SqlInternalConnectionTds);
1592                 if (null == tdsConnection) {
1593                     throw SQL.NotAvailableOnContextConnection();
1594                 }
1595                 return tdsConnection.Parser;
1596             }
1597         }
1598
1599         internal bool Asynchronous {
1600             get {
1601                 SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
1602                 return ((null != constr) ? constr.Asynchronous : SqlConnectionString.DEFAULT.Asynchronous);
1603             }
1604         }
1605
1606         //
1607         // INTERNAL METHODS
1608         //
1609         
1610         internal void ValidateConnectionForExecute(string method, SqlCommand command) {
1611             Task asyncWaitingForReconnection=_asyncWaitingForReconnection;
1612             if (asyncWaitingForReconnection!=null) {
1613                 if (!asyncWaitingForReconnection.IsCompleted) {
1614                     throw SQL.MARSUnspportedOnConnection();
1615                 }
1616                 else {
1617                     Interlocked.CompareExchange(ref _asyncWaitingForReconnection, null, asyncWaitingForReconnection);
1618                 }
1619             }
1620             if (_currentReconnectionTask != null) {
1621                 Task currentReconnectionTask = _currentReconnectionTask;
1622                 if (currentReconnectionTask != null && !currentReconnectionTask.IsCompleted) {
1623                     return; // execution will wait for this task later
1624                 }
1625             }
1626             SqlInternalConnection innerConnection = GetOpenConnection(method);
1627             innerConnection.ValidateConnectionForExecute(command);
1628         }
1629
1630         // Surround name in brackets and then escape any end bracket to protect against SQL Injection.
1631         // NOTE: if the user escapes it themselves it will not work, but this was the case in V1 as well
1632         // as native OleDb and Odbc.
1633         static internal string FixupDatabaseTransactionName(string name) {
1634             if (!ADP.IsEmpty(name)) {
1635                 return SqlServerEscapeHelper.EscapeIdentifier(name);
1636             }
1637             else {
1638                 return name;
1639             }
1640         }
1641         
1642         // If wrapCloseInAction is defined, then the action it defines will be run with the connection close action passed in as a parameter
1643         // The close action also supports being run asynchronously
1644         internal void OnError(SqlException exception, bool breakConnection, Action<Action> wrapCloseInAction) {
1645             Debug.Assert(exception != null && exception.Errors.Count != 0, "SqlConnection: OnError called with null or empty exception!");
1646
1647             // Bug fix - MDAC 49022 - connection open after failure...  Problem was parser was passing
1648             // Open as a state - because the parser's connection to the netlib was open.  We would
1649             // then set the connection state to the parser's state - which is not correct.  The only
1650             // time the connection state should change to what is passed in to this function is if
1651             // the parser is broken, then we should be closed.  Changed to passing in
1652             // TdsParserState, not ConnectionState.
1653             // fixed by [....]
1654
1655             if (breakConnection && (ConnectionState.Open == State)) {
1656
1657                 if (wrapCloseInAction != null) {
1658                     int capturedCloseCount = _closeCount;
1659
1660                     Action closeAction = () => {
1661                         if (capturedCloseCount == _closeCount) {
1662                             Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1663                             Close();
1664                         }
1665                     };
1666
1667                     wrapCloseInAction(closeAction);
1668                 }
1669                 else {
1670                     Bid.Trace("<sc.SqlConnection.OnError|INFO> %d#, Connection broken.\n", ObjectID);
1671                     Close();
1672                 }
1673             }
1674
1675             if (exception.Class >= TdsEnums.MIN_ERROR_CLASS) {
1676                 // It is an error, and should be thrown.  Class of TdsEnums.MIN_ERROR_CLASS or above is an error,
1677                 // below TdsEnums.MIN_ERROR_CLASS denotes an info message.
1678                 throw exception;
1679             }
1680             else {
1681                 // If it is a class < TdsEnums.MIN_ERROR_CLASS, it is a warning collection - so pass to handler
1682                 this.OnInfoMessage(new SqlInfoMessageEventArgs(exception));
1683             }
1684         }
1685
1686         //
1687         // PRIVATE METHODS
1688         //
1689
1690         // SxS: using Debugger.IsAttached
1691         // 
1692         [ResourceExposure(ResourceScope.None)]
1693         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1694         private void CompleteOpen() {
1695             Debug.Assert(ConnectionState.Open == State, "CompleteOpen not open");
1696             // be sure to mark as open so SqlDebugCheck can issue Query
1697
1698             // check to see if we need to hook up sql-debugging if a debugger is attached
1699             // We only need this check for Shiloh and earlier servers.
1700             if (!GetOpenConnection().IsYukonOrNewer && 
1701                     System.Diagnostics.Debugger.IsAttached) {
1702                 bool debugCheck = false;
1703                 try {
1704                     new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); // MDAC 66682, 69017
1705                     debugCheck = true;
1706                 }
1707                 catch (SecurityException e) {
1708                     ADP.TraceExceptionWithoutRethrow(e);
1709                 }
1710
1711                 if (debugCheck) {
1712                     // if we don't have Unmanaged code permission, don't check for debugging
1713                     // but let the connection be opened while under the debugger
1714                     CheckSQLDebugOnConnect();
1715                 }
1716             }
1717         }
1718     
1719         internal SqlInternalConnection GetOpenConnection() {
1720             SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
1721             if (null == innerConnection) {
1722                 throw ADP.ClosedConnectionError();
1723             }
1724             return innerConnection;
1725         }
1726
1727         internal SqlInternalConnection GetOpenConnection(string method) {
1728             DbConnectionInternal innerConnection = InnerConnection;
1729             SqlInternalConnection innerSqlConnection = (innerConnection as SqlInternalConnection);
1730             if (null == innerSqlConnection) {
1731                 throw ADP.OpenConnectionRequired(method, innerConnection.State);
1732             }
1733             return innerSqlConnection;
1734         }
1735         
1736         internal SqlInternalConnectionTds GetOpenTdsConnection() {
1737             SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1738             if (null == innerConnection) {
1739                 throw ADP.ClosedConnectionError();
1740             }
1741             return innerConnection;
1742         }
1743         
1744         internal SqlInternalConnectionTds GetOpenTdsConnection(string method) {
1745             SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
1746             if (null == innerConnection) {
1747                 throw ADP.OpenConnectionRequired(method, InnerConnection.State);
1748             }
1749             return innerConnection;
1750         }
1751
1752         internal void OnInfoMessage(SqlInfoMessageEventArgs imevent) {
1753             bool notified;
1754             OnInfoMessage(imevent, out notified);
1755         }
1756
1757         internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) {
1758             if (Bid.TraceOn) {
1759                 Debug.Assert(null != imevent, "null SqlInfoMessageEventArgs");
1760                 Bid.Trace("<sc.SqlConnection.OnInfoMessage|API|INFO> %d#, Message='%ls'\n", ObjectID, ((null != imevent) ? imevent.Message : ""));
1761             }
1762             SqlInfoMessageEventHandler handler = (SqlInfoMessageEventHandler)Events[EventInfoMessage];
1763             if (null != handler) {
1764                 notified = true;
1765                 try {
1766                     handler(this, imevent);
1767                 }
1768                 catch (Exception e) { // MDAC 53175
1769                     if (!ADP.IsCatchableOrSecurityExceptionType(e)) {
1770                         throw;
1771                     }
1772
1773                     ADP.TraceExceptionWithoutRethrow(e);
1774                 }
1775             } else {
1776                 notified = false;
1777             }
1778         }
1779
1780         //
1781         // SQL DEBUGGING SUPPORT
1782         //
1783
1784         // this only happens once per connection
1785         // SxS: using named file mapping APIs
1786         // 
1787         [ResourceExposure(ResourceScope.None)]
1788         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1789         private void CheckSQLDebugOnConnect() {
1790             IntPtr hFileMap;
1791             uint pid = (uint)SafeNativeMethods.GetCurrentProcessId();
1792
1793             string mapFileName;
1794
1795             // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
1796             if (ADP.IsPlatformNT5) {
1797                 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
1798             }
1799             else {
1800                 mapFileName = TdsEnums.SDCI_MAPFILENAME;
1801             }
1802
1803             mapFileName = mapFileName + pid.ToString(CultureInfo.InvariantCulture);
1804
1805             hFileMap = NativeMethods.OpenFileMappingA(0x4/*FILE_MAP_READ*/, false, mapFileName);
1806
1807             if (ADP.PtrZero != hFileMap) {
1808                 IntPtr pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x4/*FILE_MAP_READ*/, 0, 0, IntPtr.Zero);
1809                 if (ADP.PtrZero != pMemMap) {
1810                     SqlDebugContext sdc = new SqlDebugContext();
1811                     sdc.hMemMap = hFileMap;
1812                     sdc.pMemMap = pMemMap;
1813                     sdc.pid = pid;
1814
1815                     // optimization: if we only have to refresh memory-mapped data at connection open time
1816                     // optimization: then call here instead of in CheckSQLDebug() which gets called
1817                     // optimization: at command execution time
1818                     // RefreshMemoryMappedData(sdc);
1819
1820                     // delaying setting out global state until after we issue this first SQLDebug command so that
1821                     // we don't reentrantly call into CheckSQLDebug
1822                     CheckSQLDebug(sdc);
1823                     // now set our global state
1824                     _sdc = sdc;
1825                 }
1826             }
1827         }
1828
1829         // This overload is called by the Command object when executing stored procedures.  Note that
1830         // if SQLDebug has never been called, it is a noop.
1831         internal void CheckSQLDebug() {
1832             if (null != _sdc)
1833                 CheckSQLDebug(_sdc);
1834         }
1835
1836         // SxS: using GetCurrentThreadId
1837         [ResourceExposure(ResourceScope.None)]
1838         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1839         [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] // MDAC 66682, 69017
1840         private void CheckSQLDebug(SqlDebugContext sdc) {
1841             // check to see if debugging has been activated
1842             Debug.Assert(null != sdc, "SQL Debug: invalid null debugging context!");
1843
1844 #pragma warning disable 618
1845             uint tid = (uint)AppDomain.GetCurrentThreadId();    // Sql Debugging doesn't need fiber support;
1846 #pragma warning restore 618
1847             RefreshMemoryMappedData(sdc);
1848
1849             // 
1850
1851
1852
1853             // If we get here, the debugger must be hooked up.
1854             if (!sdc.active) {
1855                 if (sdc.fOption/*TdsEnums.SQLDEBUG_ON*/) {
1856                     // turn on
1857                     sdc.active = true;
1858                     sdc.tid = tid;
1859                     try {
1860                         IssueSQLDebug(TdsEnums.SQLDEBUG_ON, sdc.machineName, sdc.pid, sdc.dbgpid, sdc.sdiDllName, sdc.data);
1861                         sdc.tid = 0; // reset so that the first successful time through, we notify the server of the context switch
1862                     }
1863                     catch {
1864                         sdc.active = false;
1865                         throw;
1866                     }
1867                 }
1868             }
1869
1870             // be sure to pick up thread context switch, especially the first time through
1871             if (sdc.active) {
1872                 if (!sdc.fOption/*TdsEnums.SQLDEBUG_OFF*/) {
1873                     // turn off and free the memory
1874                     sdc.Dispose();
1875                     // okay if we throw out here, no state to clean up
1876                     IssueSQLDebug(TdsEnums.SQLDEBUG_OFF, null, 0, 0, null, null);
1877                 }
1878                 else {
1879                     // notify server of context change
1880                     if (sdc.tid != tid) {
1881                         sdc.tid = tid;
1882                         try {
1883                             IssueSQLDebug(TdsEnums.SQLDEBUG_CONTEXT, null, sdc.pid, sdc.tid, null, null);
1884                         }
1885                         catch {
1886                             sdc.tid = 0;
1887                             throw;
1888                         }
1889                     }
1890                 }
1891             }
1892         }
1893
1894         private void IssueSQLDebug(uint option, string machineName, uint pid, uint id, string sdiDllName, byte[] data) {
1895
1896             if (GetOpenConnection().IsYukonOrNewer) {
1897                 // 
1898                 return;
1899             }
1900
1901             // 
1902
1903             SqlCommand c = new SqlCommand(TdsEnums.SP_SDIDEBUG, this);
1904             c.CommandType = CommandType.StoredProcedure;
1905
1906             // context param
1907             SqlParameter p = new SqlParameter(null, SqlDbType.VarChar, TdsEnums.SQLDEBUG_MODE_NAMES[option].Length);
1908             p.Value = TdsEnums.SQLDEBUG_MODE_NAMES[option];
1909             c.Parameters.Add(p);
1910
1911             if (option == TdsEnums.SQLDEBUG_ON) {
1912                 // debug dll name
1913                 p = new SqlParameter(null, SqlDbType.VarChar, sdiDllName.Length);
1914                 p.Value = sdiDllName;
1915                 c.Parameters.Add(p);
1916                 // debug machine name
1917                 p = new SqlParameter(null, SqlDbType.VarChar, machineName.Length);
1918                 p.Value = machineName;
1919                 c.Parameters.Add(p);
1920             }
1921
1922             if (option != TdsEnums.SQLDEBUG_OFF) {
1923                 // client pid
1924                 p = new SqlParameter(null, SqlDbType.Int);
1925                 p.Value = pid;
1926                 c.Parameters.Add(p);
1927                 // dbgpid or tid
1928                 p = new SqlParameter(null, SqlDbType.Int);
1929                 p.Value = id;
1930                 c.Parameters.Add(p);
1931             }
1932
1933             if (option == TdsEnums.SQLDEBUG_ON) {
1934                 // debug data
1935                 p = new SqlParameter(null, SqlDbType.VarBinary, (null != data) ? data.Length : 0);
1936                 p.Value = data;
1937                 c.Parameters.Add(p);
1938             }
1939
1940             c.ExecuteNonQuery();
1941         }
1942
1943
1944         public static void ChangePassword(string connectionString, string newPassword) {
1945             IntPtr hscp;
1946             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
1947             Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
1948             try {
1949                 if (ADP.IsEmpty(connectionString)) {
1950                     throw SQL.ChangePasswordArgumentMissing("connectionString");
1951                 }
1952                 if (ADP.IsEmpty(newPassword)) {
1953                     throw SQL.ChangePasswordArgumentMissing("newPassword");
1954                 }
1955                 if (TdsEnums.MAXLEN_NEWPASSWORD < newPassword.Length) {
1956                     throw ADP.InvalidArgumentLength("newPassword", TdsEnums.MAXLEN_NEWPASSWORD);
1957                 }
1958
1959                 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential: null, accessToken: null);
1960
1961                 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
1962                 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
1963
1964                     throw SQL.ChangePasswordConflictsWithSSPI();
1965                 }
1966                 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
1967                     throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
1968                 }
1969                 if (connectionOptions.ContextConnection) {
1970                     throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
1971                 }
1972
1973                 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
1974                 permissionSet.Demand();
1975
1976                 ChangePassword(connectionString, connectionOptions, null, newPassword, null);
1977              }
1978             finally {
1979                 Bid.ScopeLeave(ref hscp) ;
1980             }
1981        }
1982
1983         public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) {
1984             IntPtr hscp;
1985             Bid.ScopeEnter(out hscp, "<sc.SqlConnection.ChangePassword|API>") ;
1986             Bid.CorrelationTrace("<sc.SqlConnection.ChangePassword|API|Correlation> ActivityID %ls\n");
1987             try {
1988                 if (ADP.IsEmpty(connectionString)) {
1989                     throw SQL.ChangePasswordArgumentMissing("connectionString");
1990                 }
1991
1992                 // check credential; not necessary to check the length of password in credential as the check is done by SqlCredential class
1993                 if (credential == null) {
1994                     throw SQL.ChangePasswordArgumentMissing("credential");
1995                 }
1996
1997                 if (newSecurePassword == null || newSecurePassword.Length == 0) {
1998                     throw SQL.ChangePasswordArgumentMissing("newSecurePassword");;
1999                 }
2000
2001                 if (!newSecurePassword.IsReadOnly()) {
2002                     throw ADP.MustBeReadOnly("newSecurePassword");
2003                 }
2004
2005                 if (TdsEnums.MAXLEN_NEWPASSWORD < newSecurePassword.Length) {
2006                     throw ADP.InvalidArgumentLength("newSecurePassword", TdsEnums.MAXLEN_NEWPASSWORD);
2007                 }
2008
2009                 SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2010
2011                 SqlConnectionString connectionOptions = SqlConnectionFactory.FindSqlConnectionOptions(key);
2012
2013                 // Check for incompatible connection string value with SqlCredential
2014                 if (!ADP.IsEmpty(connectionOptions.UserID) || !ADP.IsEmpty(connectionOptions.Password)) {
2015                     throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
2016                 }
2017
2018                 if (connectionOptions.IntegratedSecurity || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
2019                     throw SQL.ChangePasswordConflictsWithSSPI();
2020                 }
2021
2022                 if (! ADP.IsEmpty(connectionOptions.AttachDBFilename)) {
2023                     throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.AttachDBFilename);
2024                 }
2025
2026                 if (connectionOptions.ContextConnection) {
2027                     throw SQL.ChangePasswordUseOfUnallowedKey(SqlConnectionString.KEY.Context_Connection);
2028                 }
2029
2030                 System.Security.PermissionSet permissionSet = connectionOptions.CreatePermissionSet();
2031                 permissionSet.Demand();
2032
2033                 ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword);
2034             }
2035             finally {
2036                 Bid.ScopeLeave(ref hscp) ;
2037             }
2038         }
2039
2040         private static void ChangePassword(string connectionString, SqlConnectionString connectionOptions, SqlCredential credential, string newPassword, SecureString newSecurePassword ) {
2041             // note: This is the only case where we directly construt the internal connection, passing in the new password.
2042             // Normally we would simply create a regular connectoin and open it but there is no other way to pass the
2043             // new password down to the constructor. Also it would have an unwanted impact on the connection pool
2044             //
2045             using (SqlInternalConnectionTds con = new SqlInternalConnectionTds(null, connectionOptions, credential, null, newPassword, newSecurePassword, false)) {
2046                 if (!con.IsYukonOrNewer) {
2047                     throw SQL.ChangePasswordRequiresYukon();
2048                 }
2049             }
2050             SqlConnectionPoolKey key = new SqlConnectionPoolKey(connectionString, credential, accessToken: null);
2051
2052             SqlConnectionFactory.SingletonInstance.ClearPool(key);
2053         }
2054
2055         internal void RegisterForConnectionCloseNotification<T>(ref Task<T> outterTask, object value, int tag) {
2056             // Connection exists,  schedule removal, will be added to ref collection after calling ValidateAndReconnect
2057             outterTask = outterTask.ContinueWith(task => {
2058                 RemoveWeakReference(value);
2059                 return task;
2060             }, TaskScheduler.Default).Unwrap();
2061         }
2062
2063         // updates our context with any changes made to the memory-mapped data by an external process
2064         static private void RefreshMemoryMappedData(SqlDebugContext sdc) {
2065             Debug.Assert(ADP.PtrZero != sdc.pMemMap, "SQL Debug: invalid null value for pMemMap!");
2066             // copy memory mapped file contents into managed types
2067             MEMMAP memMap = (MEMMAP)Marshal.PtrToStructure(sdc.pMemMap, typeof(MEMMAP));
2068             sdc.dbgpid = memMap.dbgpid;
2069             sdc.fOption = (memMap.fOption == 1) ? true : false;
2070             // xlate ansi byte[] -> managed strings
2071             Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2072             sdc.machineName = cp.GetString(memMap.rgbMachineName, 0, memMap.rgbMachineName.Length);
2073             sdc.sdiDllName = cp.GetString(memMap.rgbDllName, 0, memMap.rgbDllName.Length);
2074             // just get data reference
2075             sdc.data = memMap.rgbData;
2076         }
2077
2078         public void ResetStatistics() {
2079             if (IsContextConnection) {
2080                 throw SQL.NotAvailableOnContextConnection();
2081             }
2082
2083             if (null != Statistics) {
2084                 Statistics.Reset();
2085                 if (ConnectionState.Open == State) {
2086                     // update timestamp;
2087                     ADP.TimerCurrent(out _statistics._openTimestamp);
2088                 }
2089             }
2090         }
2091
2092         public IDictionary RetrieveStatistics() {
2093             if (IsContextConnection) {
2094                 throw SQL.NotAvailableOnContextConnection();
2095             }
2096
2097             if (null != Statistics) {
2098                 UpdateStatistics();
2099                 return Statistics.GetHashtable();
2100             }
2101             else {
2102                 return new SqlStatistics().GetHashtable();
2103             }
2104         }
2105
2106         private void UpdateStatistics() {
2107             if (ConnectionState.Open == State) {
2108                 // update timestamp
2109                 ADP.TimerCurrent(out _statistics._closeTimestamp);
2110             }
2111             // delegate the rest of the work to the SqlStatistics class
2112             Statistics.UpdateStatistics();
2113         }
2114
2115         //
2116         // UDT SUPPORT
2117         //
2118
2119         private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) {
2120             Debug.Assert(TypeSystemAssemblyVersion != null, "TypeSystemAssembly should be set !");
2121             if (string.Compare(asmRef.Name, "Microsoft.SqlServer.Types", StringComparison.OrdinalIgnoreCase) == 0) {
2122                 if (Bid.TraceOn) {
2123                     if (asmRef.Version!=TypeSystemAssemblyVersion) {
2124                         Bid.Trace("<sc.SqlConnection.ResolveTypeAssembly> SQL CLR type version change: Server sent %ls, client will instantiate %ls", 
2125                             asmRef.Version.ToString(), TypeSystemAssemblyVersion.ToString());
2126                     }
2127                 }
2128                 asmRef.Version = TypeSystemAssemblyVersion;
2129             }
2130             try {
2131                 return Assembly.Load(asmRef);
2132             }
2133             catch (Exception e) {
2134                 if (throwOnError || !ADP.IsCatchableExceptionType(e)) {
2135                     throw;
2136                 }
2137                 else {
2138                     return null;
2139                 };
2140             }
2141         }
2142
2143         // 
2144         internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) {
2145             if (metaData.udtType == null) { // If null, we have not obtained extended info.
2146                 Debug.Assert(!ADP.IsEmpty(metaData.udtAssemblyQualifiedName), "Unexpected state on GetUDTInfo");
2147                 // Parameter throwOnError determines whether exception from Assembly.Load is thrown.
2148                 metaData.udtType =                
2149                     Type.GetType(typeName:metaData.udtAssemblyQualifiedName, assemblyResolver:asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver:null, throwOnError: fThrow); 
2150
2151                 if (fThrow && metaData.udtType == null) {
2152                     // 
2153                     throw SQL.UDTUnexpectedResult(metaData.udtAssemblyQualifiedName); 
2154                 }
2155             }
2156         }
2157
2158         internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull) {
2159             if (returnDBNull && ADP.IsNull(value)) {
2160                 return DBNull.Value;
2161             }
2162
2163             object o = null;
2164
2165             // Since the serializer doesn't handle nulls...
2166             if (ADP.IsNull(value)) {
2167                 Type t = metaData.udtType;
2168                 Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!");
2169                 o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, new Object[]{}, CultureInfo.InvariantCulture);
2170                 Debug.Assert(o != null);
2171                 return o;
2172             }
2173             else {
2174
2175                 MemoryStream stm = new MemoryStream((byte[]) value);
2176
2177                 o = SerializationHelperSql9.Deserialize(stm, metaData.udtType);
2178
2179                 Debug.Assert(o != null, "object could NOT be created");
2180                 return o;
2181             }
2182         }
2183
2184         internal byte[] GetBytes(object o) {
2185             Microsoft.SqlServer.Server.Format format  = Microsoft.SqlServer.Server.Format.Native;
2186             int    maxSize = 0;
2187             return GetBytes(o, out format, out maxSize);
2188         }
2189
2190         internal byte[] GetBytes(object o, out Microsoft.SqlServer.Server.Format format, out int maxSize) {
2191             SqlUdtInfo attr = AssemblyCache.GetInfoFromType(o.GetType());
2192             maxSize = attr.MaxByteSize;
2193             format  = attr.SerializationFormat;
2194
2195             if (maxSize < -1 || maxSize >= UInt16.MaxValue) { // Do we need this?  Is this the right place?
2196                 throw new InvalidOperationException(o.GetType() + ": invalid Size");
2197             }
2198
2199             byte[] retval;
2200
2201             using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) {
2202                 SerializationHelperSql9.Serialize(stm, o);
2203                 retval = stm.ToArray();
2204             }
2205             return retval;
2206         }
2207     } // SqlConnection
2208
2209     // 
2210
2211
2212
2213
2214
2215     [
2216     ComVisible(true),
2217     ClassInterface(ClassInterfaceType.None),
2218     Guid("afef65ad-4577-447a-a148-83acadd3d4b9"),
2219     ]
2220     [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2221     public sealed class SQLDebugging: ISQLDebug {
2222
2223         // Security stuff
2224         const int STANDARD_RIGHTS_REQUIRED = (0x000F0000);
2225         const int DELETE = (0x00010000);
2226         const int READ_CONTROL = (0x00020000);
2227         const int WRITE_DAC = (0x00040000);
2228         const int WRITE_OWNER = (0x00080000);
2229         const int SYNCHRONIZE = (0x00100000);
2230         const int FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x000001FF);
2231         const uint GENERIC_READ = (0x80000000);
2232         const uint GENERIC_WRITE = (0x40000000);
2233         const uint GENERIC_EXECUTE = (0x20000000);
2234         const uint GENERIC_ALL = (0x10000000);
2235
2236         const int SECURITY_DESCRIPTOR_REVISION = (1);
2237         const int ACL_REVISION = (2);
2238
2239         const int SECURITY_AUTHENTICATED_USER_RID = (0x0000000B);
2240         const int SECURITY_LOCAL_SYSTEM_RID = (0x00000012);
2241         const int SECURITY_BUILTIN_DOMAIN_RID = (0x00000020);
2242         const int SECURITY_WORLD_RID = (0x00000000);
2243         const byte SECURITY_NT_AUTHORITY = 5;
2244         const int DOMAIN_GROUP_RID_ADMINS = (0x00000200);
2245         const int DOMAIN_ALIAS_RID_ADMINS = (0x00000220);
2246
2247         const int sizeofSECURITY_ATTRIBUTES = 12; // sizeof(SECURITY_ATTRIBUTES);
2248         const int sizeofSECURITY_DESCRIPTOR = 20; // sizeof(SECURITY_DESCRIPTOR);
2249         const int sizeofACCESS_ALLOWED_ACE = 12; // sizeof(ACCESS_ALLOWED_ACE);
2250         const int sizeofACCESS_DENIED_ACE = 12; // sizeof(ACCESS_DENIED_ACE);
2251         const int sizeofSID_IDENTIFIER_AUTHORITY = 6; // sizeof(SID_IDENTIFIER_AUTHORITY)
2252         const int sizeofACL = 8; // sizeof(ACL);
2253
2254         private IntPtr CreateSD(ref IntPtr pDacl) {
2255             IntPtr pSecurityDescriptor = IntPtr.Zero;
2256             IntPtr pUserSid = IntPtr.Zero;
2257             IntPtr pAdminSid = IntPtr.Zero;
2258             IntPtr pNtAuthority = IntPtr.Zero;
2259             int cbAcl = 0;
2260             bool status = false;
2261
2262             pNtAuthority = Marshal.AllocHGlobal(sizeofSID_IDENTIFIER_AUTHORITY);
2263             if (pNtAuthority == IntPtr.Zero)
2264                 goto cleanup;
2265             Marshal.WriteInt32(pNtAuthority, 0, 0);
2266             Marshal.WriteByte(pNtAuthority, 4, 0);
2267             Marshal.WriteByte(pNtAuthority, 5, SECURITY_NT_AUTHORITY);
2268
2269             status =
2270             NativeMethods.AllocateAndInitializeSid(
2271             pNtAuthority,
2272             (byte)1,
2273             SECURITY_AUTHENTICATED_USER_RID,
2274             0,
2275             0,
2276             0,
2277             0,
2278             0,
2279             0,
2280             0,
2281             ref pUserSid);
2282
2283             if (!status || pUserSid == IntPtr.Zero) {
2284                 goto cleanup;
2285             }
2286             status =
2287             NativeMethods.AllocateAndInitializeSid(
2288             pNtAuthority,
2289             (byte)2,
2290             SECURITY_BUILTIN_DOMAIN_RID,
2291             DOMAIN_ALIAS_RID_ADMINS,
2292             0,
2293             0,
2294             0,
2295             0,
2296             0,
2297             0,
2298             ref pAdminSid);
2299
2300             if (!status || pAdminSid == IntPtr.Zero) {
2301                 goto cleanup;
2302             }
2303             status = false;
2304             pSecurityDescriptor = Marshal.AllocHGlobal(sizeofSECURITY_DESCRIPTOR);
2305             if (pSecurityDescriptor == IntPtr.Zero) {
2306                 goto cleanup;
2307             }
2308             for (int i = 0; i < sizeofSECURITY_DESCRIPTOR; i++)
2309                 Marshal.WriteByte(pSecurityDescriptor, i, (byte)0);
2310             cbAcl = sizeofACL
2311             + (2 * (sizeofACCESS_ALLOWED_ACE))
2312             + sizeofACCESS_DENIED_ACE
2313             + NativeMethods.GetLengthSid(pUserSid)
2314             + NativeMethods.GetLengthSid(pAdminSid);
2315
2316             pDacl = Marshal.AllocHGlobal(cbAcl);
2317             if (pDacl == IntPtr.Zero) {
2318                 goto cleanup;
2319             }
2320             // rights must be added in a certain order.  Namely, deny access first, then add access
2321             if (NativeMethods.InitializeAcl(pDacl, cbAcl, ACL_REVISION))
2322                 if (NativeMethods.AddAccessDeniedAce(pDacl, ACL_REVISION, WRITE_DAC, pUserSid))
2323                     if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_READ, pUserSid))
2324                         if (NativeMethods.AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_ALL, pAdminSid))
2325                             if (NativeMethods.InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
2326                                 if (NativeMethods.SetSecurityDescriptorDacl(pSecurityDescriptor, true, pDacl, false)) {
2327                                     status = true;
2328                                 }
2329
2330             cleanup :
2331             if (pNtAuthority != IntPtr.Zero) {
2332                 Marshal.FreeHGlobal(pNtAuthority);
2333             }
2334             if (pAdminSid != IntPtr.Zero)
2335                 NativeMethods.FreeSid(pAdminSid);
2336             if (pUserSid != IntPtr.Zero)
2337                 NativeMethods.FreeSid(pUserSid);
2338             if (status)
2339                 return pSecurityDescriptor;
2340             else {
2341                 if (pSecurityDescriptor != IntPtr.Zero) {
2342                     Marshal.FreeHGlobal(pSecurityDescriptor);
2343                 }
2344             }
2345             return IntPtr.Zero;
2346         }
2347
2348         // SxS: using file mapping API (CreateFileMapping)
2349         // 
2350         [ResourceExposure(ResourceScope.None)]
2351         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2352         bool ISQLDebug.SQLDebug(int dwpidDebugger, int dwpidDebuggee, [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2353         [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName, int dwOption, int cbData, byte[] rgbData) {
2354             bool result = false;
2355             IntPtr hFileMap = IntPtr.Zero;
2356             IntPtr pMemMap = IntPtr.Zero;
2357             IntPtr pSecurityDescriptor = IntPtr.Zero;
2358             IntPtr pSecurityAttributes = IntPtr.Zero;
2359             IntPtr pDacl = IntPtr.Zero;
2360
2361             // validate the structure
2362             if (null == pszMachineName || null == pszSDIDLLName)
2363                 return false;
2364
2365             if (pszMachineName.Length > TdsEnums.SDCI_MAX_MACHINENAME ||
2366             pszSDIDLLName.Length > TdsEnums.SDCI_MAX_DLLNAME)
2367                 return false;
2368
2369             // note that these are ansi strings
2370             Encoding cp = System.Text.Encoding.GetEncoding(TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE);
2371             byte[] rgbMachineName = cp.GetBytes(pszMachineName);
2372             byte[] rgbSDIDLLName = cp.GetBytes(pszSDIDLLName);
2373
2374             if (null != rgbData && cbData > TdsEnums.SDCI_MAX_DATA)
2375                 return false;
2376
2377             string mapFileName;
2378
2379             // If Win2k or later, prepend "Global\\" to enable this to work through TerminalServices.
2380             if (ADP.IsPlatformNT5) {
2381                 mapFileName = "Global\\" + TdsEnums.SDCI_MAPFILENAME;
2382             }
2383             else {
2384                 mapFileName = TdsEnums.SDCI_MAPFILENAME;
2385             }
2386
2387             mapFileName = mapFileName + dwpidDebuggee.ToString(CultureInfo.InvariantCulture);
2388
2389             // Create Security Descriptor
2390             pSecurityDescriptor = CreateSD(ref pDacl);
2391             pSecurityAttributes = Marshal.AllocHGlobal(sizeofSECURITY_ATTRIBUTES);
2392             if ((pSecurityDescriptor == IntPtr.Zero) || (pSecurityAttributes == IntPtr.Zero))
2393                 return false;
2394
2395             Marshal.WriteInt32(pSecurityAttributes, 0, sizeofSECURITY_ATTRIBUTES); // nLength = sizeof(SECURITY_ATTRIBUTES)
2396             Marshal.WriteIntPtr(pSecurityAttributes, 4, pSecurityDescriptor); // lpSecurityDescriptor = pSecurityDescriptor
2397             Marshal.WriteInt32(pSecurityAttributes, 8, 0); // bInheritHandle = FALSE
2398             hFileMap = NativeMethods.CreateFileMappingA(
2399             ADP.InvalidPtr/*INVALID_HANDLE_VALUE*/,
2400             pSecurityAttributes,
2401             0x4/*PAGE_READWRITE*/,
2402             0,
2403             Marshal.SizeOf(typeof(MEMMAP)),
2404             mapFileName);
2405
2406             if (IntPtr.Zero == hFileMap) {
2407                 goto cleanup;
2408             }
2409
2410
2411             pMemMap = NativeMethods.MapViewOfFile(hFileMap, 0x6/*FILE_MAP_READ|FILE_MAP_WRITE*/, 0, 0, IntPtr.Zero);
2412
2413             if (IntPtr.Zero == pMemMap) {
2414                 goto cleanup;
2415             }
2416
2417             // copy data to memory-mapped file
2418             // layout of MEMMAP structure is:
2419             // uint dbgpid
2420             // uint fOption
2421             // byte[32] machineName
2422             // byte[16] sdiDllName
2423             // uint dbData
2424             // byte[255] vData
2425             int offset = 0;
2426             Marshal.WriteInt32(pMemMap, offset, (int)dwpidDebugger);
2427             offset += 4;
2428             Marshal.WriteInt32(pMemMap, offset, (int)dwOption);
2429             offset += 4;
2430             Marshal.Copy(rgbMachineName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbMachineName.Length);
2431             offset += TdsEnums.SDCI_MAX_MACHINENAME;
2432             Marshal.Copy(rgbSDIDLLName, 0, ADP.IntPtrOffset(pMemMap, offset), rgbSDIDLLName.Length);
2433             offset += TdsEnums.SDCI_MAX_DLLNAME;
2434             Marshal.WriteInt32(pMemMap, offset, (int)cbData);
2435             offset += 4;
2436             if (null != rgbData) {
2437                 Marshal.Copy(rgbData, 0, ADP.IntPtrOffset(pMemMap, offset), (int)cbData);
2438             }
2439             NativeMethods.UnmapViewOfFile(pMemMap);
2440             result = true;
2441         cleanup :
2442             if (result == false) {
2443                 if (hFileMap != IntPtr.Zero)
2444                     NativeMethods.CloseHandle(hFileMap);
2445             }
2446             if (pSecurityAttributes != IntPtr.Zero)
2447                 Marshal.FreeHGlobal(pSecurityAttributes);
2448             if (pSecurityDescriptor != IntPtr.Zero)
2449                 Marshal.FreeHGlobal(pSecurityDescriptor);
2450             if (pDacl != IntPtr.Zero)
2451                 Marshal.FreeHGlobal(pDacl);
2452             return result;
2453         }
2454     }
2455
2456     // this is a private interface to com+ users
2457     // do not change this guid
2458     [
2459     ComImport,
2460     ComVisible(true),
2461     Guid("6cb925bf-c3c0-45b3-9f44-5dd67c7b7fe8"),
2462     InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
2463     BestFitMapping(false, ThrowOnUnmappableChar = true),
2464     ]
2465     interface ISQLDebug {
2466
2467         [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
2468         bool SQLDebug(
2469         int dwpidDebugger,
2470         int dwpidDebuggee,
2471         [MarshalAs(UnmanagedType.LPStr)] string pszMachineName,
2472         [MarshalAs(UnmanagedType.LPStr)] string pszSDIDLLName,
2473         int dwOption,
2474         int cbData,
2475         byte[] rgbData);
2476     }
2477
2478     sealed class SqlDebugContext: IDisposable {
2479         // context data
2480         internal uint pid = 0;
2481         internal uint tid = 0;
2482         internal bool active = false;
2483         // memory-mapped data
2484         internal IntPtr pMemMap = ADP.PtrZero;
2485         internal IntPtr hMemMap = ADP.PtrZero;
2486         internal uint dbgpid = 0;
2487         internal bool fOption = false;
2488         internal string machineName = null;
2489         internal string sdiDllName = null;
2490         internal byte[] data = null;
2491
2492         public void Dispose() {
2493             Dispose(true);
2494             GC.SuppressFinalize(this);
2495         }
2496
2497         // using CloseHandle and UnmapViewOfFile - no exposure
2498         [ResourceExposure(ResourceScope.None)]
2499         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
2500         private void Dispose(bool disposing) {
2501             if (disposing) {
2502                 // Nothing to do here
2503                 ;
2504             }
2505             if (pMemMap != IntPtr.Zero) {
2506                 NativeMethods.UnmapViewOfFile(pMemMap);
2507                 pMemMap = IntPtr.Zero;
2508             }
2509             if (hMemMap != IntPtr.Zero) {
2510                 NativeMethods.CloseHandle(hMemMap);
2511                 hMemMap = IntPtr.Zero;
2512             }
2513             active = false;
2514         }
2515         
2516         ~SqlDebugContext() {
2517                 Dispose(false);
2518         }
2519
2520     }
2521
2522     // native interop memory mapped structure for sdi debugging
2523     [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
2524     internal struct MEMMAP {
2525         [MarshalAs(UnmanagedType.U4)]
2526         internal uint dbgpid; // id of debugger
2527         [MarshalAs(UnmanagedType.U4)]
2528         internal uint fOption; // 1 - start debugging, 0 - stop debugging
2529         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
2530         internal byte[] rgbMachineName;
2531         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
2532         internal byte[] rgbDllName;
2533         [MarshalAs(UnmanagedType.U4)]
2534         internal uint cbData;
2535         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
2536         internal byte[] rgbData;
2537     }
2538 } // System.Data.SqlClient namespace
2539
2540