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