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