SqlConnection.cs 110 KB

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