oledbconnectionstring.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. //------------------------------------------------------------------------------
  2. // <copyright file="oledbconnectionstring.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. namespace System.Data.OleDb {
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Collections.Specialized;
  13. using System.Data;
  14. using System.Data.Common;
  15. using System.Diagnostics;
  16. using System.Globalization;
  17. using System.IO;
  18. using System.Security;
  19. using System.Security.Permissions;
  20. using System.Text;
  21. using Microsoft.Win32;
  22. using System.Runtime.Versioning;
  23. internal struct SchemaSupport {
  24. internal Guid _schemaRowset;
  25. internal int _restrictions;
  26. }
  27. internal sealed class OleDbConnectionString : DbConnectionOptions {
  28. // instances of this class are intended to be immutable, i.e readonly
  29. // used by pooling classes so it is much easier to verify correctness
  30. // when not worried about the class being modified during execution
  31. internal static class KEY {
  32. internal const string Asynchronous_Processing = "asynchronous processing";
  33. internal const string Connect_Timeout = "connect timeout";
  34. internal const string Data_Provider = "data provider";
  35. internal const string Data_Source = "data source";
  36. internal const string Extended_Properties = "extended properties";
  37. internal const string File_Name = "file name";
  38. internal const string Initial_Catalog = "initial catalog";
  39. internal const string Ole_DB_Services = "ole db services";
  40. internal const string Persist_Security_Info = "persist security info";
  41. internal const string Prompt = "prompt";
  42. internal const string Provider = "provider";
  43. internal const string RemoteProvider = "remote provider";
  44. internal const string WindowHandle = "window handle";
  45. }
  46. // registry key and dword value entry for udl pooling
  47. private static class UDL {
  48. internal const string Header = "\xfeff[oledb]\r\n; Everything after this line is an OLE DB initstring\r\n";
  49. internal const string Location = "SOFTWARE\\Microsoft\\DataAccess\\Udl Pooling";
  50. internal const string Pooling = "Cache Size";
  51. static internal volatile bool _PoolSizeInit;
  52. static internal int _PoolSize;
  53. static internal volatile Dictionary<string,string> _Pool;
  54. static internal object _PoolLock = new object();
  55. }
  56. private static class VALUES {
  57. internal const string NoPrompt = "noprompt";
  58. }
  59. // set during ctor
  60. internal readonly bool PossiblePrompt;
  61. internal readonly string ActualConnectionString; // cached value passed to GetDataSource
  62. private readonly string _expandedConnectionString;
  63. internal SchemaSupport[] _schemaSupport;
  64. internal int _sqlSupport;
  65. internal bool _supportMultipleResults;
  66. internal bool _supportIRow;
  67. internal bool _hasSqlSupport;
  68. internal bool _hasSupportMultipleResults, _hasSupportIRow;
  69. private int _oledbServices;
  70. // these are cached delegates (per unique connectionstring)
  71. internal UnsafeNativeMethods.IUnknownQueryInterface DangerousDataSourceIUnknownQueryInterface;
  72. internal UnsafeNativeMethods.IDBInitializeInitialize DangerousIDBInitializeInitialize;
  73. internal UnsafeNativeMethods.IDBCreateSessionCreateSession DangerousIDBCreateSessionCreateSession;
  74. internal UnsafeNativeMethods.IDBCreateCommandCreateCommand DangerousIDBCreateCommandCreateCommand;
  75. // since IDBCreateCommand interface may not be supported for a particular provider (only IOpenRowset)
  76. // we cache that fact rather than call QueryInterface on every call to Open
  77. internal bool HaveQueriedForCreateCommand;
  78. // SxS: if user specifies a value for "File Name=" (UDL) in connection string, OleDbConnectionString will load the connection string
  79. // from the UDL file. The UDL file is opened as FileMode.Open, FileAccess.Read, FileShare.Read, allowing concurrent access to it.
  80. [ResourceExposure(ResourceScope.None)]
  81. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  82. internal OleDbConnectionString(string connectionString, bool validate) : base(connectionString) {
  83. string prompt = this[KEY.Prompt];
  84. PossiblePrompt = ((!ADP.IsEmpty(prompt) && (0 != String.Compare(prompt, VALUES.NoPrompt, StringComparison.OrdinalIgnoreCase)))
  85. || !ADP.IsEmpty(this[KEY.WindowHandle]));
  86. if (!IsEmpty) {
  87. string udlConnectionString = null;
  88. if (!validate) {
  89. int position = 0;
  90. string udlFileName = null;
  91. _expandedConnectionString = ExpandDataDirectories(ref udlFileName, ref position);
  92. if (!ADP.IsEmpty(udlFileName)) { // fail via new FileStream vs. GetFullPath
  93. udlFileName = ADP.GetFullPath(udlFileName); // MDAC 82833
  94. }
  95. if (null != udlFileName) {
  96. udlConnectionString = LoadStringFromStorage(udlFileName);
  97. if (!ADP.IsEmpty(udlConnectionString)) {
  98. _expandedConnectionString = _expandedConnectionString.Substring(0, position) + udlConnectionString + ';' + _expandedConnectionString.Substring(position);
  99. }
  100. }
  101. }
  102. if (validate || ADP.IsEmpty(udlConnectionString)) {
  103. ActualConnectionString = ValidateConnectionString(connectionString);
  104. }
  105. }
  106. }
  107. internal int ConnectTimeout {
  108. get { return base.ConvertValueToInt32(KEY.Connect_Timeout, ADP.DefaultConnectionTimeout); }
  109. }
  110. internal string DataSource {
  111. get { return base.ConvertValueToString(KEY.Data_Source, ADP.StrEmpty); }
  112. }
  113. internal string InitialCatalog {
  114. get { return base.ConvertValueToString(KEY.Initial_Catalog, ADP.StrEmpty); }
  115. }
  116. internal string Provider {
  117. get {
  118. Debug.Assert(!ADP.IsEmpty(this[KEY.Provider]), "no Provider");
  119. return this[KEY.Provider];
  120. }
  121. }
  122. internal int OleDbServices {
  123. get {
  124. return _oledbServices;
  125. }
  126. }
  127. internal SchemaSupport[] SchemaSupport { // OleDbConnection.GetSchemaRowsetInformation
  128. get { return _schemaSupport; }
  129. set { _schemaSupport = value; }
  130. }
  131. protected internal override System.Security.PermissionSet CreatePermissionSet() {
  132. System.Security.PermissionSet permissionSet;
  133. if (PossiblePrompt) {
  134. permissionSet = new NamedPermissionSet("FullTrust");
  135. }
  136. else {
  137. permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None);
  138. permissionSet.AddPermission(new OleDbPermission(this));
  139. }
  140. return permissionSet;
  141. }
  142. protected internal override string Expand() {
  143. if (null != _expandedConnectionString) {
  144. return _expandedConnectionString;
  145. }
  146. else {
  147. return base.Expand();
  148. }
  149. }
  150. internal int GetSqlSupport(OleDbConnection connection) {
  151. int sqlSupport = _sqlSupport;
  152. if (!_hasSqlSupport) {
  153. object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_SQLSUPPORT);
  154. if (value is Int32) { // not OleDbPropertyStatus
  155. sqlSupport = (int) value;
  156. }
  157. _sqlSupport = sqlSupport;
  158. _hasSqlSupport = true;
  159. }
  160. return sqlSupport;
  161. }
  162. internal bool GetSupportIRow(OleDbConnection connection, OleDbCommand command) {
  163. bool supportIRow = _supportIRow;
  164. if (!_hasSupportIRow) {
  165. object value = command.GetPropertyValue(OleDbPropertySetGuid.Rowset, ODB.DBPROP_IRow);
  166. // SQLOLEDB always returns VARIANT_FALSE for DBPROP_IROW, so base the answer on existance
  167. supportIRow = !(value is OleDbPropertyStatus);
  168. _supportIRow = supportIRow;
  169. _hasSupportIRow = true;
  170. }
  171. return supportIRow;
  172. }
  173. internal bool GetSupportMultipleResults(OleDbConnection connection) {
  174. bool supportMultipleResults = _supportMultipleResults;
  175. if (!_hasSupportMultipleResults) {
  176. object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_MULTIPLERESULTS);
  177. if (value is Int32) {// not OleDbPropertyStatus
  178. supportMultipleResults = (ODB.DBPROPVAL_MR_NOTSUPPORTED != (int) value);
  179. }
  180. _supportMultipleResults = supportMultipleResults;
  181. _hasSupportMultipleResults = true;
  182. }
  183. return supportMultipleResults;
  184. }
  185. static private int UdlPoolSize { // MDAC 69925
  186. // SxS: UdpPoolSize reads registry value to get the pool size
  187. [ResourceExposure(ResourceScope.None)]
  188. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  189. get {
  190. int poolsize = UDL._PoolSize;
  191. if (!UDL._PoolSizeInit) {
  192. object value = ADP.LocalMachineRegistryValue(UDL.Location, UDL.Pooling);
  193. if (value is Int32) {
  194. poolsize = (int) value;
  195. poolsize = ((0 < poolsize) ? poolsize : 0);
  196. UDL._PoolSize = poolsize;
  197. }
  198. UDL._PoolSizeInit = true;
  199. }
  200. return poolsize;
  201. }
  202. }
  203. [ResourceExposure(ResourceScope.Machine)]
  204. [ResourceConsumption(ResourceScope.Machine)]
  205. static private string LoadStringFromStorage(string udlfilename) {
  206. string udlConnectionString = null;
  207. Dictionary<string,string> udlcache = UDL._Pool;
  208. if ((null == udlcache) || !udlcache.TryGetValue(udlfilename, out udlConnectionString)) {
  209. udlConnectionString = LoadStringFromFileStorage(udlfilename);
  210. if (null != udlConnectionString) {
  211. Debug.Assert(!ADP.IsEmpty(udlfilename), "empty filename didn't fail");
  212. if (0 < UdlPoolSize) {
  213. Debug.Assert(udlfilename == ADP.GetFullPath(udlfilename), "only cache full path filenames"); // MDAC 82833
  214. if (null == udlcache) {
  215. udlcache = new Dictionary<string,string>();
  216. udlcache[udlfilename] = udlConnectionString;
  217. lock(UDL._PoolLock) {
  218. if (null != UDL._Pool) {
  219. udlcache = UDL._Pool;
  220. }
  221. else {
  222. UDL._Pool = udlcache;
  223. udlcache = null;
  224. }
  225. }
  226. }
  227. if (null != udlcache) {
  228. lock(udlcache) {
  229. udlcache[udlfilename] = udlConnectionString;
  230. }
  231. }
  232. }
  233. }
  234. }
  235. return udlConnectionString;
  236. }
  237. [ResourceExposure(ResourceScope.Machine)]
  238. [ResourceConsumption(ResourceScope.Machine)]
  239. static private string LoadStringFromFileStorage(string udlfilename) {
  240. // Microsoft Data Link File Format
  241. // The first two lines of a .udl file must have exactly the following contents in order to work properly:
  242. // [oledb]
  243. // ; Everything after this line is an OLE DB initstring
  244. //
  245. string connectionString = null;
  246. Exception failure = null;
  247. try {
  248. int hdrlength = ADP.CharSize*UDL.Header.Length;
  249. using(FileStream fstream = new FileStream(udlfilename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
  250. long length = fstream.Length;
  251. if (length < hdrlength || (0 != length%ADP.CharSize)) {
  252. failure = ADP.InvalidUDL();
  253. }
  254. else {
  255. byte[] bytes = new Byte[hdrlength];
  256. int count = fstream.Read(bytes, 0, bytes.Length);
  257. if (count < hdrlength) {
  258. failure = ADP.InvalidUDL();
  259. }
  260. else if (System.Text.Encoding.Unicode.GetString(bytes, 0, hdrlength) != UDL.Header) {
  261. failure = ADP.InvalidUDL();
  262. }
  263. else { // please verify header before allocating memory block for connection string
  264. bytes = new Byte[length - hdrlength];
  265. count = fstream.Read(bytes, 0, bytes.Length);
  266. connectionString = System.Text.Encoding.Unicode.GetString(bytes, 0, count);
  267. }
  268. }
  269. }
  270. }
  271. catch(Exception e) {
  272. //
  273. if (!ADP.IsCatchableExceptionType(e)) {
  274. throw;
  275. }
  276. throw ADP.UdlFileError(e);
  277. }
  278. if (null != failure) {
  279. throw failure;
  280. }
  281. return connectionString.Trim();
  282. }
  283. [ResourceExposure(ResourceScope.None)] // reads OleDbServices value for the provider
  284. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  285. private string ValidateConnectionString(string connectionString) {
  286. if (ConvertValueToBoolean(KEY.Asynchronous_Processing, false)) {
  287. throw ODB.AsynchronousNotSupported();
  288. }
  289. int connectTimeout = ConvertValueToInt32(KEY.Connect_Timeout, 0);
  290. if (connectTimeout < 0) {
  291. throw ADP.InvalidConnectTimeoutValue();
  292. }
  293. string progid = ConvertValueToString(KEY.Data_Provider, null); // MDAC 71923
  294. if (null != progid) {
  295. progid = progid.Trim();
  296. if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
  297. ValidateProvider(progid);
  298. }
  299. }
  300. progid = ConvertValueToString(KEY.RemoteProvider, null); // MDAC 71923
  301. if (null != progid) {
  302. progid = progid.Trim();
  303. if (0 < progid.Length) { // don't fail on empty 'Data Provider' value
  304. ValidateProvider(progid);
  305. }
  306. }
  307. progid = ConvertValueToString(KEY.Provider, ADP.StrEmpty).Trim();
  308. ValidateProvider(progid); // will fail on empty 'Provider' value
  309. // SQLBU VSTS 59322: initialize to default
  310. // If the value is not provided in connection string and OleDbServices registry key has not been set by the provider,
  311. // the default for the provider is -1 (all services are ON).
  312. // our default is -13, we turn off ODB.DBPROPVAL_OS_AGR_AFTERSESSION and ODB.DBPROPVAL_OS_CLIENTCURSOR flags
  313. _oledbServices = DbConnectionStringDefaults.OleDbServices;
  314. bool hasOleDBServices = (base.ContainsKey(KEY.Ole_DB_Services) && !ADP.IsEmpty((string)base[KEY.Ole_DB_Services]));
  315. if (!hasOleDBServices) { // don't touch registry if they have OLE DB Services
  316. string classid = (string) ADP.ClassesRootRegistryValue(progid + "\\CLSID", String.Empty);
  317. if ((null != classid) && (0 < classid.Length)) {
  318. // CLSID detection of 'Microsoft OLE DB Provider for ODBC Drivers'
  319. Guid classidProvider = new Guid(classid);
  320. if (ODB.CLSID_MSDASQL == classidProvider) {
  321. throw ODB.MSDASQLNotSupported();
  322. }
  323. object tmp = ADP.ClassesRootRegistryValue("CLSID\\{" + classidProvider.ToString("D", CultureInfo.InvariantCulture) + "}", ODB.OLEDB_SERVICES);
  324. if (null != tmp) {
  325. // @devnote: some providers like MSDataShape don't have the OLEDB_SERVICES value
  326. // the MSDataShape provider doesn't support the 'Ole Db Services' keyword
  327. // hence, if the value doesn't exist - don't prepend to string
  328. try {
  329. _oledbServices = (int)tmp;
  330. }
  331. catch(InvalidCastException e) {
  332. ADP.TraceExceptionWithoutRethrow(e);
  333. }
  334. _oledbServices &= ~(ODB.DBPROPVAL_OS_AGR_AFTERSESSION | ODB.DBPROPVAL_OS_CLIENTCURSOR); // NT 347436, MDAC 58606
  335. StringBuilder builder = new StringBuilder();
  336. builder.Append(KEY.Ole_DB_Services);
  337. builder.Append("=");
  338. builder.Append(_oledbServices.ToString(CultureInfo.InvariantCulture));
  339. builder.Append(";");
  340. builder.Append(connectionString);
  341. connectionString = builder.ToString();
  342. }
  343. }
  344. }
  345. else {
  346. // SQLBU VSTS 59322: parse the Ole Db Services value from connection string
  347. _oledbServices = ConvertValueToInt32(KEY.Ole_DB_Services, DbConnectionStringDefaults.OleDbServices);
  348. }
  349. return connectionString;
  350. }
  351. internal static bool IsMSDASQL(string progid) {
  352. return (("msdasql" == progid) || progid.StartsWith("msdasql.", StringComparison.Ordinal) || ("microsoft ole db provider for odbc drivers" == progid));
  353. }
  354. static private void ValidateProvider(string progid) {
  355. if (ADP.IsEmpty(progid)) {
  356. throw ODB.NoProviderSpecified();
  357. }
  358. if (ODB.MaxProgIdLength <= progid.Length) { // MDAC 63151
  359. throw ODB.InvalidProviderSpecified();
  360. }
  361. progid = progid.ToLower(CultureInfo.InvariantCulture);
  362. if (IsMSDASQL(progid)) {
  363. // fail msdasql even if not on the machine.
  364. throw ODB.MSDASQLNotSupported();
  365. }
  366. }
  367. static internal void ReleaseObjectPool() {
  368. UDL._PoolSizeInit = false;
  369. UDL._Pool = null;
  370. }
  371. }
  372. }