diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 7e64b8fd9e..91ac3e6fee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -55,6 +55,9 @@ Microsoft\Data\Common\NameValuePair.cs + + Microsoft\Data\Common\DbConnectionOptions.Common.cs + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs @@ -438,9 +441,6 @@ - - Microsoft\Data\Common\DbConnectionOptions.Common.cs - Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs index c90bb1f3bf..35440e9e11 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Text; @@ -12,104 +10,25 @@ namespace Microsoft.Data.Common { internal partial class DbConnectionOptions { - // instances of this class are intended to be immutable, i.e readonly - // used by pooling classes so it is much easier to verify correctness - // when not worried about the class being modified during execution - - public DbConnectionOptions(string connectionString, Dictionary synonyms) - { - _parsetable = new Dictionary(); - _usersConnectionString = ((null != connectionString) ? connectionString : ""); - - // first pass on parsing, initial syntax check - if (0 < _usersConnectionString.Length) - { - _keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, false); - HasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd)); - HasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID)); - } - } - - protected DbConnectionOptions(DbConnectionOptions connectionOptions) - { // Clone used by SqlConnectionString - _usersConnectionString = connectionOptions._usersConnectionString; - _parsetable = connectionOptions._parsetable; - _keyChain = connectionOptions._keyChain; - HasPasswordKeyword = connectionOptions.HasPasswordKeyword; - HasUserIdKeyword = connectionOptions.HasUserIdKeyword; - } - - public bool IsEmpty => _keyChain == null; - - internal bool TryGetParsetableValue(string key, out string value) => _parsetable.TryGetValue(key, out value); - - // same as Boolean, but with SSPI thrown in as valid yes - public bool ConvertValueToIntegratedSecurity() + internal string ExpandAttachDbFileName(string replacementValue) { - string value; - return _parsetable.TryGetValue(KEY.Integrated_Security, out value) && value != null ? - ConvertValueToIntegratedSecurityInternal(value) : - false; - } + int copyPosition = 0; - internal bool ConvertValueToIntegratedSecurityInternal(string stringValue) - { - if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes")) - return true; - else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no")) - return false; - else + StringBuilder builder = new(_usersConnectionString.Length); + for (NameValuePair current = _keyChain; null != current; current = current.Next) { - string tmp = stringValue.Trim(); // Remove leading & trailing whitespace. - if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes")) - return true; - else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no")) - return false; + if (string.Equals(current.Name, DbConnectionStringKeywords.AttachDBFilename, StringComparison.InvariantCultureIgnoreCase)) + { + builder.Append($"{current.Name}={replacementValue};"); + } else { - throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security); + builder.Append(_usersConnectionString, copyPosition, current.Length); } + copyPosition += current.Length; } - } - - public int ConvertValueToInt32(string keyName, int defaultValue) - { - string value; - return _parsetable.TryGetValue(keyName, out value) && value != null ? - ConvertToInt32Internal(keyName, value) : - defaultValue; - } - - internal static int ConvertToInt32Internal(string keyname, string stringValue) - { - try - { - return int.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture); - } - catch (FormatException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - catch (OverflowException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - } - public string ConvertValueToString(string keyName, string defaultValue) - { - string value; - return _parsetable.TryGetValue(keyName, out value) && value != null ? value : defaultValue; - } - - public bool ContainsKey(string keyword) - { - return _parsetable.ContainsKey(keyword); - } - - protected internal virtual string Expand() - { - return _usersConnectionString; + return builder.ToString(); } // SxS notes: @@ -151,25 +70,5 @@ internal static string ExpandDataDirectory(string keyword, string value) return fullPath; } - internal string ExpandAttachDbFileName(string replacementValue) - { - int copyPosition = 0; - - StringBuilder builder = new StringBuilder(_usersConnectionString.Length); - for (NameValuePair current = _keyChain; null != current; current = current.Next) - { - if (current.Name == KEY.AttachDBFileName) - { - builder.Append($"{KEY.AttachDBFileName}={replacementValue};"); - } - else - { - builder.Append(_usersConnectionString, copyPosition, current.Length); - } - copyPosition += current.Length; - } - - return builder.ToString(); - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index abad0809cb..3946a09f33 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1820,7 +1820,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) && - (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) && + (!connectionOptions._hasUserIdKeyword || !connectionOptions._hasPasswordKeyword) && _credential == null) { throw SQL.CredentialsNotProvided(connectionOptions.Authentication); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 819d796312..598367b0a0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -456,32 +456,32 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G throw SQL.AuthenticationAndIntegratedSecurity(); } - if (Authentication == SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated && HasPasswordKeyword) + if (Authentication == SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated && _hasPasswordKeyword) { throw SQL.IntegratedWithPassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && _hasPasswordKeyword) { throw SQL.InteractiveWithPassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (HasUserIdKeyword || HasPasswordKeyword)) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (_hasUserIdKeyword || _hasPasswordKeyword)) { throw SQL.DeviceFlowWithUsernamePassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index d46baf18e2..9b85ced97f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -2117,7 +2117,7 @@ internal void OnLoginAck(SqlLoginAck rec) /// Federated Authentication Info. internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert((ConnectionOptions.HasUserIdKeyword && ConnectionOptions.HasPasswordKeyword) + Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) || _credential != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index ae61c39607..9a5615c00c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -110,6 +110,9 @@ Microsoft\Data\Common\NameValuePair.cs + + Microsoft\Data\Common\DbConnectionOptions.Common.cs + Microsoft\Data\Sql\SqlNotificationRequest.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs index c51f4f2730..57391584d2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DBConnectionString.cs @@ -23,16 +23,16 @@ internal sealed class DBConnectionString private static class KEY { - internal const string Password = "password"; - internal const string PersistSecurityInfo = "persist security info"; - internal const string Pwd = "pwd"; + internal const string Password = DbConnectionStringKeywords.Password; + internal const string PersistSecurityInfo = DbConnectionStringKeywords.PersistSecurityInfo; + internal const string Pwd = DbConnectionStringSynonyms.Pwd; }; // this class is serializable with Everett, so ugly field names can't be changed readonly private string _encryptedUsersConnectionString; // hash of unique keys to values - readonly private Hashtable _parsetable; + readonly private Dictionary _parsetable; // a linked list of key/value and their length in _encryptedUsersConnectionString readonly private NameValuePair _keychain; @@ -52,21 +52,21 @@ private static class KEY readonly private string _encryptedActualConnectionString; #pragma warning restore 169 - internal DBConnectionString(string value, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool useOdbcRules) - : this(new DbConnectionOptions(value, synonyms, useOdbcRules), restrictions, behavior, synonyms, false) + internal DBConnectionString(string value, string restrictions, KeyRestrictionBehavior behavior, Dictionary synonyms, bool useOdbcRules) + : this(new DbConnectionOptions(value, synonyms), restrictions, behavior, synonyms, false) { // useOdbcRules is only used to parse the connection string, not to parse restrictions because values don't apply there // the hashtable doesn't need clone since it isn't shared with anything else } internal DBConnectionString(DbConnectionOptions connectionOptions) - : this(connectionOptions, (string)null, KeyRestrictionBehavior.AllowOnly, (Hashtable)null, true) + : this(connectionOptions, (string)null, KeyRestrictionBehavior.AllowOnly, null, true) { // used by DBDataPermission to convert from DbConnectionOptions to DBConnectionString // since backward compatibility requires Everett level classes } - private DBConnectionString(DbConnectionOptions connectionOptions, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool mustCloneDictionary) + private DBConnectionString(DbConnectionOptions connectionOptions, string restrictions, KeyRestrictionBehavior behavior, Dictionary synonyms, bool mustCloneDictionary) { // used by DBDataPermission Debug.Assert(null != connectionOptions, "null connectionOptions"); switch (behavior) @@ -81,9 +81,9 @@ private DBConnectionString(DbConnectionOptions connectionOptions, string restric // grab all the parsed details from DbConnectionOptions _encryptedUsersConnectionString = connectionOptions.UsersConnectionString(false); - _hasPassword = connectionOptions.HasPasswordKeyword; + _hasPassword = connectionOptions._hasPasswordKeyword; _parsetable = connectionOptions.Parsetable; - _keychain = connectionOptions.KeyChain; + _keychain = connectionOptions._keyChain; // we do not want to serialize out user password unless directed so by "persist security info=true" // otherwise all instances of user's password will be replaced with "*" @@ -94,7 +94,7 @@ private DBConnectionString(DbConnectionOptions connectionOptions, string restric { // clone the hashtable to replace user's password/pwd value with "*" // we only need to clone if coming from DbConnectionOptions and password exists - _parsetable = (Hashtable)_parsetable.Clone(); + _parsetable = new Dictionary(_parsetable, _parsetable.Comparer); } // different than Everett in that instead of removing password/pwd from @@ -467,7 +467,7 @@ static private string[] NoDuplicateUnion(string[] a, string[] b) return restrictionValues; } - private static string[] ParseRestrictions(string restrictions, Hashtable synonyms) + private static string[] ParseRestrictions(string restrictions, Dictionary synonyms) { #if DEBUG SqlClientEventSource.Log.TryAdvancedTraceEvent(" Restrictions='{0}'", restrictions); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs index 07431834fb..364f256fdc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Common/DbConnectionOptions.cs @@ -2,183 +2,28 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Runtime.Versioning; +using System.Security; +using System.Text; + namespace Microsoft.Data.Common { - - using System; - using System.Collections; - using System.Diagnostics; - using System.Globalization; - using System.Runtime.Versioning; - using System.Text; - using System.Text.RegularExpressions; - using Microsoft.Data.SqlClient; - - internal class DbConnectionOptions + internal partial class DbConnectionOptions { - // instances of this class are intended to be immutable, i.e readonly - // used by pooling classes so it is much easier to verify correctness - // when not worried about the class being modified during execution - -#if DEBUG - /*private const string ConnectionStringPatternV1 = - "[\\s;]*" - +"(?([^=\\s]|\\s+[^=\\s]|\\s+==|==)+)" - + "\\s*=(?!=)\\s*" - +"(?(" - + "(" + "\"" + "([^\"]|\"\")*" + "\"" + ")" - + "|" - + "(" + "'" + "([^']|'')*" + "'" + ")" - + "|" - + "(" + "(?![\"'])" + "([^\\s;]|\\s+[^\\s;])*" + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+)" // allow any visible character for keyname except '=' which must quoted as '==' - + "\\s*=(?!=)\\s*" // the equal sign divides the key and value parts - + "(?" - + "(\"([^\"\u0000]|\"\")*\")" // double quoted string, " must be quoted as "" - + "|" - + "('([^'\u0000]|'')*')" // single quoted string, ' must be quoted as '' - + "|" - + "((?![\"'\\s])" // unquoted value must not start with " or ' or space, would also like = but too late to change - + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted - + "(?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}])+)" // allow any visible character for keyname except '=' - + "\\s*=\\s*" // the equal sign divides the key and value parts - + "(?" - + "(\\{([^\\}\u0000]|\\}\\})*\\})" // quoted string, starts with { and ends with } - + "|" - + "((?![\\{\\s])" // unquoted value must not start with { or space, would also like = but too late to change - + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted - - + ")" // VSTFDEVDIV 94761: although the spec does not allow {} - // embedded within a value, the retail code does. - // + "(? _parsetable[keyword]; - // known connection string common synonyms - private static class SYNONYM - { - internal const string Pwd = "pwd"; - internal const string UID = "uid"; - }; - - private readonly string _usersConnectionString; - private readonly Hashtable _parsetable; - internal readonly NameValuePair KeyChain; - internal readonly bool HasPasswordKeyword; - internal readonly bool HasUserIdKeyword; - - // differences between OleDb and Odbc - // ODBC: - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcsqldriverconnect.asp - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbcsql/od_odbc_d_4x4k.asp - // do not support == -> = in keywords - // first key-value pair wins - // quote values using \{ and \}, only driver= and pwd= appear to generically allow quoting - // do not strip quotes from value, or add quotes except for driver keyword - // OLEDB: - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp - // support == -> = in keywords - // last key-value pair wins - // quote values using \" or \' - // strip quotes from value - internal readonly bool UseOdbcRules; - - private System.Security.PermissionSet _permissionset; - - // called by derived classes that may cache based on connectionString - public DbConnectionOptions(string connectionString) - : this(connectionString, null, false) - { - } - - // synonyms hashtable is meant to be read-only translation of parsed string - // keywords/synonyms to a known keyword string - public DbConnectionOptions(string connectionString, Hashtable synonyms, bool useOdbcRules) - { - UseOdbcRules = useOdbcRules; - _parsetable = new Hashtable(); - _usersConnectionString = ((null != connectionString) ? connectionString : ""); + private PermissionSet _permissionset; - // first pass on parsing, initial syntax check - if (0 < _usersConnectionString.Length) - { - KeyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, UseOdbcRules); - HasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd)); - HasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID)); - } - } + protected internal virtual PermissionSet CreatePermissionSet() => null; - protected DbConnectionOptions(DbConnectionOptions connectionOptions) - { // Clone used by SqlConnectionString - _usersConnectionString = connectionOptions._usersConnectionString; - HasPasswordKeyword = connectionOptions.HasPasswordKeyword; - HasUserIdKeyword = connectionOptions.HasUserIdKeyword; - UseOdbcRules = connectionOptions.UseOdbcRules; - _parsetable = connectionOptions._parsetable; - KeyChain = connectionOptions.KeyChain; - } - - - public string UsersConnectionString(bool hidePassword) - { - return UsersConnectionString(hidePassword, false); - } - - private string UsersConnectionString(bool hidePassword, bool forceHidePassword) + internal void DemandPermission() { - string connectionString = _usersConnectionString; - if (HasPasswordKeyword && (forceHidePassword || (hidePassword && !HasPersistablePassword))) + if (_permissionset is null) { - ReplacePasswordPwd(out connectionString, false); + _permissionset = CreatePermissionSet(); } - return ((null != connectionString) ? connectionString : ""); - } - - internal string UsersConnectionStringForTrace() - { - return UsersConnectionString(true, true); + _permissionset.Demand(); } internal bool HasBlankPassword @@ -189,60 +34,61 @@ internal bool HasBlankPassword { if (_parsetable.ContainsKey(KEY.Password)) { - return ADP.IsEmpty((string)_parsetable[KEY.Password]); + return ADP.IsEmpty(_parsetable[KEY.Password]); } else if (_parsetable.ContainsKey(SYNONYM.Pwd)) { - return ADP.IsEmpty((string)_parsetable[SYNONYM.Pwd]); // MDAC 83097 + return ADP.IsEmpty(_parsetable[SYNONYM.Pwd]); // MDAC 83097 } else { - return ((_parsetable.ContainsKey(KEY.User_ID) && !ADP.IsEmpty((string)_parsetable[KEY.User_ID])) || (_parsetable.ContainsKey(SYNONYM.UID) && !ADP.IsEmpty((string)_parsetable[SYNONYM.UID]))); + return (_parsetable.ContainsKey(KEY.User_ID) && !ADP.IsEmpty(_parsetable[KEY.User_ID])) || + (_parsetable.ContainsKey(SYNONYM.UID) && !ADP.IsEmpty(_parsetable[SYNONYM.UID])); } } return false; } } - internal bool HasPersistablePassword + internal string ExpandKeyword(string keyword, string replacementValue) { - get + // preserve duplicates, updated keyword value with replacement value + // if keyword not specified, append to end of the string + bool expanded = false; + int copyPosition = 0; + + StringBuilder builder = new(_usersConnectionString.Length); + for (NameValuePair current = _keyChain; null != current; current = current.Next) { - if (HasPasswordKeyword) + if ((current.Name == keyword) && (current.Value == this[keyword])) + { + // only replace the parse end-result value instead of all values + // so that when duplicate-keywords occur other original values remain in place + AppendKeyValuePairBuilder(builder, current.Name, replacementValue); + builder.Append(';'); + expanded = true; + } + else { - return ConvertValueToBoolean(KEY.Persist_Security_Info, false); + builder.Append(_usersConnectionString, copyPosition, current.Length); } - return true; // no password means persistable password so we don't have to munge + copyPosition += current.Length; } - } - - public bool IsEmpty - { - get { return (null == KeyChain); } - } - - internal Hashtable Parsetable - { - get { return _parsetable; } - } - - public ICollection Keys - { - get { return _parsetable.Keys; } - } - public string this[string keyword] - { - get { return (string)_parsetable[keyword]; } + if (!expanded) + { + AppendKeyValuePairBuilder(builder, keyword, replacementValue); + } + return builder.ToString(); } - internal static void AppendKeyValuePairBuilder(StringBuilder builder, string keyName, string keyValue, bool useOdbcRules) + internal static void AppendKeyValuePairBuilder(StringBuilder builder, string keyName, string keyValue, bool useOdbcRules = false) { - ADP.CheckArgumentNull(builder, "builder"); - ADP.CheckArgumentLength(keyName, "keyName"); + ADP.CheckArgumentNull(builder, nameof(builder)); + ADP.CheckArgumentLength(keyName, nameof(keyName)); - if ((null == keyName) || !ConnectionStringValidKeyRegex.IsMatch(keyName)) + if ((null == keyName) || !s_connectionStringValidKeyRegex.IsMatch(keyName)) { throw ADP.InvalidKeyname(keyName); } @@ -271,8 +117,8 @@ internal static void AppendKeyValuePairBuilder(StringBuilder builder, string key if (useOdbcRules) { if ((0 < keyValue.Length) && - (('{' == keyValue[0]) || (0 <= keyValue.IndexOf(';')) || (0 == String.Compare(DbConnectionStringKeywords.Driver, keyName, StringComparison.OrdinalIgnoreCase))) && - !ConnectionStringQuoteOdbcValueRegex.IsMatch(keyValue)) + (('{' == keyValue[0]) || (0 <= keyValue.IndexOf(';')) || (0 == string.Compare(DbConnectionStringKeywords.Driver, keyName, StringComparison.OrdinalIgnoreCase))) && + !s_connectionStringQuoteOdbcValueRegex.IsMatch(keyValue)) { // always quote Driver value (required for ODBC Version 2.65 and earlier) // always quote values that contain a ';' @@ -283,7 +129,7 @@ internal static void AppendKeyValuePairBuilder(StringBuilder builder, string key builder.Append(keyValue); } } - else if (ConnectionStringQuoteValueRegex.IsMatch(keyValue)) + else if (s_connectionStringQuoteValueRegex.IsMatch(keyValue)) { // -> builder.Append(keyValue); @@ -310,128 +156,6 @@ internal static void AppendKeyValuePairBuilder(StringBuilder builder, string key } } - public bool ConvertValueToBoolean(string keyName, bool defaultValue) - { - object value = _parsetable[keyName]; - if (null == value) - { - return defaultValue; - } - return ConvertValueToBooleanInternal(keyName, (string)value); - } - - internal static bool ConvertValueToBooleanInternal(string keyName, string stringValue) - { - if (CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes")) - return true; - else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no")) - return false; - else - { - string tmp = stringValue.Trim(); // Remove leading & trailing white space. - if (CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes")) - return true; - else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no")) - return false; - else - { - throw ADP.InvalidConnectionOptionValue(keyName); - } - } - } - - // same as Boolean, but with SSPI thrown in as valid yes - public bool ConvertValueToIntegratedSecurity() - { - object value = _parsetable[KEY.Integrated_Security]; - if (null == value) - { - return false; - } - return ConvertValueToIntegratedSecurityInternal((string)value); - } - - internal bool ConvertValueToIntegratedSecurityInternal(string stringValue) - { - if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes")) - return true; - else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no")) - return false; - else - { - string tmp = stringValue.Trim(); // Remove leading & trailing white space. - if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes")) - return true; - else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no")) - return false; - else - { - throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security); - } - } - } - - public int ConvertValueToInt32(string keyName, int defaultValue) - { - object value = _parsetable[keyName]; - if (null == value) - { - return defaultValue; - } - return ConvertToInt32Internal(keyName, (string)value); - } - - internal static int ConvertToInt32Internal(string keyname, string stringValue) - { - try - { - return System.Int32.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture); - } - catch (FormatException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - catch (OverflowException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - } - - public string ConvertValueToString(string keyName, string defaultValue) - { - string value = (string)_parsetable[keyName]; - return ((null != value) ? value : defaultValue); - } - - static private bool CompareInsensitiveInvariant(string strvalue, string strconst) - { - return (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst)); - } - - public bool ContainsKey(string keyword) - { - return _parsetable.ContainsKey(keyword); - } - - protected internal virtual System.Security.PermissionSet CreatePermissionSet() - { - return null; - } - - internal void DemandPermission() - { - if (null == _permissionset) - { - _permissionset = CreatePermissionSet(); - } - _permissionset.Demand(); - } - - protected internal virtual string Expand() - { - return _usersConnectionString; - } - // SxS notes: // * this method queries "DataDirectory" value from the current AppDomain. // This string is used for to replace "!DataDirectory!" values in the connection string, it is not considered as an "exposed resource". @@ -496,660 +220,5 @@ internal static string ExpandDataDirectory(string keyword, string value, ref str } return fullPath; } - - internal string ExpandDataDirectories(ref string filename, ref int position) - { - string value = null; - StringBuilder builder = new StringBuilder(_usersConnectionString.Length); - string datadir = null; - - int copyPosition = 0; - bool expanded = false; - - for (NameValuePair current = KeyChain; null != current; current = current.Next) - { - value = current.Value; - - // remove duplicate keyswords from connectionstring - //if ((object)this[current.Name] != (object)value) { - // expanded = true; - // copyPosition += current.Length; - // continue; - //} - - // There is a set of keywords we explictly do NOT want to expand |DataDirectory| on - if (UseOdbcRules) - { - switch (current.Name) - { - case DbConnectionOptionKeywords.Driver: - case DbConnectionOptionKeywords.Pwd: - case DbConnectionOptionKeywords.UID: - break; - default: - value = ExpandDataDirectory(current.Name, value, ref datadir); - break; - } - } - else - { - switch (current.Name) - { - case DbConnectionOptionKeywords.Provider: - case DbConnectionOptionKeywords.DataProvider: - case DbConnectionOptionKeywords.RemoteProvider: - case DbConnectionOptionKeywords.ExtendedProperties: - case DbConnectionOptionKeywords.UserID: - case DbConnectionOptionKeywords.Password: - case DbConnectionOptionKeywords.UID: - case DbConnectionOptionKeywords.Pwd: - break; - default: - value = ExpandDataDirectory(current.Name, value, ref datadir); - break; - } - } - if (null == value) - { - value = current.Value; - } - if (UseOdbcRules || (DbConnectionOptionKeywords.FileName != current.Name)) - { - if (value != current.Value) - { - expanded = true; - AppendKeyValuePairBuilder(builder, current.Name, value, UseOdbcRules); - builder.Append(';'); - } - else - { - builder.Append(_usersConnectionString, copyPosition, current.Length); - } - } - else - { - // strip out 'File Name=myconnection.udl' for OleDb - // remembering is value for which UDL file to open - // and where to insert the strnig - expanded = true; - filename = value; - position = builder.Length; - } - copyPosition += current.Length; - } - - if (expanded) - { - value = builder.ToString(); - } - else - { - value = null; - } - return value; - } - - internal string ExpandKeyword(string keyword, string replacementValue) - { - // preserve duplicates, updated keyword value with replacement value - // if keyword not specified, append to end of the string - bool expanded = false; - int copyPosition = 0; - - StringBuilder builder = new StringBuilder(_usersConnectionString.Length); - for (NameValuePair current = KeyChain; null != current; current = current.Next) - { - if ((current.Name == keyword) && (current.Value == this[keyword])) - { - // only replace the parse end-result value instead of all values - // so that when duplicate-keywords occur other original values remain in place - AppendKeyValuePairBuilder(builder, current.Name, replacementValue, UseOdbcRules); - builder.Append(';'); - expanded = true; - } - else - { - builder.Append(_usersConnectionString, copyPosition, current.Length); - } - copyPosition += current.Length; - } - - if (!expanded) - { - // TODO: technically for ODBC it should be prepended but not using the method from ODBC - Debug.Assert(!UseOdbcRules, "ExpandKeyword not ready for Odbc"); - AppendKeyValuePairBuilder(builder, keyword, replacementValue, UseOdbcRules); - } - return builder.ToString(); - } - -#if DEBUG - [System.Diagnostics.Conditional("DEBUG")] - private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Hashtable synonyms) - { - if (SqlClientEventSource.Log.IsAdvancedTraceOn()) - { - Debug.Assert(keyname == keyname.ToLower(CultureInfo.InvariantCulture), "missing ToLower"); - string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); - - if ((KEY.Password != realkeyname) && (SYNONYM.Pwd != realkeyname)) - { - // don't trace passwords ever! - if (null != keyvalue) - { - SqlClientEventSource.Log.AdvancedTraceEvent(" KeyName='{0}', KeyValue='{1}'", keyname, keyvalue); - } - else - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" KeyName='{0}'", keyname); - } - } - } - } -#endif - - static private string GetKeyName(StringBuilder buffer) - { - int count = buffer.Length; - while ((0 < count) && Char.IsWhiteSpace(buffer[count - 1])) - { - count--; // trailing whitespace - } - return buffer.ToString(0, count).ToLower(CultureInfo.InvariantCulture); - } - - static private string GetKeyValue(StringBuilder buffer, bool trimWhitespace) - { - int count = buffer.Length; - int index = 0; - if (trimWhitespace) - { - while ((index < count) && Char.IsWhiteSpace(buffer[index])) - { - index++; // leading whitespace - } - while ((0 < count) && Char.IsWhiteSpace(buffer[count - 1])) - { - count--; // trailing whitespace - } - } - return buffer.ToString(index, count - index); - } - - // transition states used for parsing - private enum ParserState - { - NothingYet = 1, //start point - Key, - KeyEqual, - KeyEnd, - UnquotedValue, - DoubleQuoteValue, - DoubleQuoteValueQuote, - SingleQuoteValue, - SingleQuoteValueQuote, - BraceQuoteValue, - BraceQuoteValueQuote, - QuotedValueEnd, - NullTermination, - }; - - static internal int GetKeyValuePair(string connectionString, int currentPosition, StringBuilder buffer, bool useOdbcRules, out string keyname, out string keyvalue) - { - int startposition = currentPosition; - - buffer.Length = 0; - keyname = null; - keyvalue = null; - - char currentChar = '\0'; - - ParserState parserState = ParserState.NothingYet; - int length = connectionString.Length; - for (; currentPosition < length; ++currentPosition) - { - currentChar = connectionString[currentPosition]; - - switch (parserState) - { - case ParserState.NothingYet: // [\\s;]* - if ((';' == currentChar) || Char.IsWhiteSpace(currentChar)) - { - continue; - } - if ('\0' == currentChar) - { parserState = ParserState.NullTermination; continue; } // MDAC 83540 - if (Char.IsControl(currentChar)) - { throw ADP.ConnectionStringSyntax(startposition); } - startposition = currentPosition; - if ('=' != currentChar) - { // MDAC 86902 - parserState = ParserState.Key; - break; - } - else - { - parserState = ParserState.KeyEqual; - continue; - } - - case ParserState.Key: // (?([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+) - if ('=' == currentChar) - { parserState = ParserState.KeyEqual; continue; } - if (Char.IsWhiteSpace(currentChar)) - { break; } - if (Char.IsControl(currentChar)) - { throw ADP.ConnectionStringSyntax(startposition); } - break; - - case ParserState.KeyEqual: // \\s*=(?!=)\\s* - if (!useOdbcRules && '=' == currentChar) - { parserState = ParserState.Key; break; } - keyname = GetKeyName(buffer); - if (ADP.IsEmpty(keyname)) - { throw ADP.ConnectionStringSyntax(startposition); } - buffer.Length = 0; - parserState = ParserState.KeyEnd; - goto case ParserState.KeyEnd; - - case ParserState.KeyEnd: - if (Char.IsWhiteSpace(currentChar)) - { continue; } - if (useOdbcRules) - { - if ('{' == currentChar) - { parserState = ParserState.BraceQuoteValue; break; } - } - else - { - if ('\'' == currentChar) - { parserState = ParserState.SingleQuoteValue; continue; } - if ('"' == currentChar) - { parserState = ParserState.DoubleQuoteValue; continue; } - } - if (';' == currentChar) - { goto ParserExit; } - if ('\0' == currentChar) - { goto ParserExit; } - if (Char.IsControl(currentChar)) - { throw ADP.ConnectionStringSyntax(startposition); } - parserState = ParserState.UnquotedValue; - break; - - case ParserState.UnquotedValue: // "((?![\"'\\s])" + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" + "(?"); - Debug.Assert(value1 == value2, "ParseInternal code vs. regex mismatch keyvalue <" + value1 + "> <" + value2 + ">"); - } - - } - catch (ArgumentException f) - { - if (null != e) - { - string msg1 = e.Message; - string msg2 = f.Message; - - const string KeywordNotSupportedMessagePrefix = "Keyword not supported:"; - const string WrongFormatMessagePrefix = "Format of the initialization string"; - bool isEquivalent = (msg1 == msg2); - if (!isEquivalent) - { - // VSTFDEVDIV 479587: we also accept cases were Regex parser (debug only) reports "wrong format" and - // retail parsing code reports format exception in different location or "keyword not supported" - if (msg2.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal)) - { - if (msg1.StartsWith(KeywordNotSupportedMessagePrefix, StringComparison.Ordinal) || msg1.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal)) - { - isEquivalent = true; - } - } - } - Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <" + msg1 + "> <" + msg2 + ">"); - } - else - { - Debug.Assert(false, "ParseInternal code vs regex throw mismatch " + f.Message); - } - e = null; - } - if (null != e) - { - Debug.Assert(false, "ParseInternal code threw exception vs regex mismatch"); - } - } -#endif - private static NameValuePair ParseInternal(Hashtable parsetable, string connectionString, bool buildChain, Hashtable synonyms, bool firstKey) - { - Debug.Assert(null != connectionString, "null connectionstring"); - StringBuilder buffer = new StringBuilder(); - NameValuePair localKeychain = null, keychain = null; -#if DEBUG - try - { -#endif - int nextStartPosition = 0; - int endPosition = connectionString.Length; - while (nextStartPosition < endPosition) - { - int startPosition = nextStartPosition; - - string keyname, keyvalue; - nextStartPosition = GetKeyValuePair(connectionString, startPosition, buffer, firstKey, out keyname, out keyvalue); - if (ADP.IsEmpty(keyname)) - { - // if (nextStartPosition != endPosition) { throw; } - break; - } -#if DEBUG - DebugTraceKeyValuePair(keyname, keyvalue, synonyms); - - Debug.Assert(IsKeyNameValid(keyname), "ParseFailure, invalid keyname"); - Debug.Assert(IsValueValidInternal(keyvalue), "parse failure, invalid keyvalue"); -#endif - string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); - if (!IsKeyNameValid(realkeyname)) - { - throw ADP.KeywordNotSupported(keyname); - } - if (!firstKey || !parsetable.Contains(realkeyname)) - { - parsetable[realkeyname] = keyvalue; // last key-value pair wins (or first) - } - - if (null != localKeychain) - { - localKeychain = localKeychain.Next = new NameValuePair(realkeyname, keyvalue, nextStartPosition - startPosition); - } - else if (buildChain) - { // first time only - don't contain modified chain from UDL file - keychain = localKeychain = new NameValuePair(realkeyname, keyvalue, nextStartPosition - startPosition); - } - } -#if DEBUG - } - catch (ArgumentException e) - { - ParseComparison(parsetable, connectionString, synonyms, firstKey, e); - throw; - } - ParseComparison(parsetable, connectionString, synonyms, firstKey, null); -#endif - return keychain; - } - - internal NameValuePair ReplacePasswordPwd(out string constr, bool fakePassword) - { - bool expanded = false; - int copyPosition = 0; - NameValuePair head = null, tail = null, next = null; - StringBuilder builder = new StringBuilder(_usersConnectionString.Length); - for (NameValuePair current = KeyChain; null != current; current = current.Next) - { - if ((KEY.Password != current.Name) && (SYNONYM.Pwd != current.Name)) - { - builder.Append(_usersConnectionString, copyPosition, current.Length); - if (fakePassword) - { - next = new NameValuePair(current.Name, current.Value, current.Length); - } - } - else if (fakePassword) - { // replace user password/pwd value with * - const string equalstar = "=*;"; - builder.Append(current.Name).Append(equalstar); - next = new NameValuePair(current.Name, "*", current.Name.Length + equalstar.Length); - expanded = true; - } - else - { // drop the password/pwd completely in returning for user - expanded = true; - } - - if (fakePassword) - { - if (null != tail) - { - tail = tail.Next = next; - } - else - { - tail = head = next; - } - } - copyPosition += current.Length; - } - Debug.Assert(expanded, "password/pwd was not removed"); - constr = builder.ToString(); - return head; - } - - internal static void ValidateKeyValuePair(string keyword, string value) - { - if ((null == keyword) || !ConnectionStringValidKeyRegex.IsMatch(keyword)) - { - throw ADP.InvalidKeyname(keyword); - } - if ((null != value) && !ConnectionStringValidValueRegex.IsMatch(value)) - { - throw ADP.InvalidValue(keyword); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index a6e417f18f..919f8f2c4d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -2059,7 +2059,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) && - (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) && + (!connectionOptions._hasUserIdKeyword || !connectionOptions._hasPasswordKeyword) && _credential == null) { throw SQL.CredentialsNotProvided(connectionOptions.Authentication); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index 7cde4cf58e..d62177ec09 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -4,6 +4,7 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Runtime.Versioning; @@ -219,7 +220,7 @@ internal static class TRANSACIONBINDING internal const string ExplicitUnbind = "Explicit Unbind"; } - static private Hashtable _sqlClientSynonyms; + private static Dictionary s_sqlClientSynonyms; static private Hashtable _netlibMapping; private readonly bool _integratedSecurity; @@ -281,7 +282,7 @@ internal static class TRANSACIONBINDING // SxS: reading Software\\Microsoft\\MSSQLServer\\Client\\SuperSocketNetLib\Encrypt value from registry [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] - internal SqlConnectionString(string connectionString) : base(connectionString, GetParseSynonyms(), false) + internal SqlConnectionString(string connectionString) : base(connectionString, GetParseSynonyms()) { bool runningInProc = InOutOfProcHelper.InProc; @@ -352,10 +353,10 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G // When using a context connection, we need to ensure that no // other connection string keywords are specified. - foreach (DictionaryEntry entry in Parsetable) + foreach (KeyValuePair entry in Parsetable) { - if ((string)entry.Key != KEY.Context_Connection && - (string)entry.Key != KEY.Type_System_Version) + if (entry.Key != KEY.Context_Connection && + entry.Key != KEY.Type_System_Version) { throw SQL.ContextAllowsLimitedKeywords(); } @@ -555,32 +556,32 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G throw SQL.AuthenticationAndIntegratedSecurity(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && (HasUserIdKeyword || HasPasswordKeyword)) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && (_hasUserIdKeyword || _hasPasswordKeyword)) { throw SQL.IntegratedWithUserIDAndPassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && (HasPasswordKeyword)) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive && (_hasPasswordKeyword)) { throw SQL.InteractiveWithPassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (HasUserIdKeyword || HasPasswordKeyword)) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow && (_hasUserIdKeyword || _hasPasswordKeyword)) { throw SQL.DeviceFlowWithUsernamePassword(); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); } - if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && HasPasswordKeyword) + if (Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault && _hasPasswordKeyword) { throw SQL.NonInteractiveWithPassword(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } @@ -783,89 +784,91 @@ private static bool CompareHostName(ref string host, string name, bool fixup) // this hashtable is meant to be read-only translation of parsed string // keywords/synonyms to a known keyword string - internal static Hashtable GetParseSynonyms() + internal static Dictionary GetParseSynonyms() { - - Hashtable hash = _sqlClientSynonyms; - if (null == hash) + Dictionary synonyms = s_sqlClientSynonyms; + if (synonyms is null) { - hash = new Hashtable(SqlConnectionStringBuilder.KeywordsCount + SynonymCount); - hash.Add(KEY.ApplicationIntent, KEY.ApplicationIntent); - hash.Add(KEY.Application_Name, KEY.Application_Name); - hash.Add(KEY.AttachDBFilename, KEY.AttachDBFilename); - hash.Add(KEY.PoolBlockingPeriod, KEY.PoolBlockingPeriod); - hash.Add(KEY.Connect_Timeout, KEY.Connect_Timeout); - hash.Add(KEY.Command_Timeout, KEY.Command_Timeout); - hash.Add(KEY.Connection_Reset, KEY.Connection_Reset); - hash.Add(KEY.Context_Connection, KEY.Context_Connection); - hash.Add(KEY.Current_Language, KEY.Current_Language); - hash.Add(KEY.Data_Source, KEY.Data_Source); - hash.Add(KEY.Encrypt, KEY.Encrypt); - hash.Add(KEY.Enlist, KEY.Enlist); - hash.Add(KEY.FailoverPartner, KEY.FailoverPartner); - hash.Add(KEY.Initial_Catalog, KEY.Initial_Catalog); - hash.Add(KEY.Integrated_Security, KEY.Integrated_Security); - hash.Add(KEY.Load_Balance_Timeout, KEY.Load_Balance_Timeout); - hash.Add(KEY.MARS, KEY.MARS); - hash.Add(KEY.Max_Pool_Size, KEY.Max_Pool_Size); - hash.Add(KEY.Min_Pool_Size, KEY.Min_Pool_Size); - hash.Add(KEY.MultiSubnetFailover, KEY.MultiSubnetFailover); - hash.Add(KEY.TransparentNetworkIPResolution, KEY.TransparentNetworkIPResolution); - hash.Add(KEY.Network_Library, KEY.Network_Library); - hash.Add(KEY.Packet_Size, KEY.Packet_Size); - hash.Add(KEY.Password, KEY.Password); - hash.Add(KEY.Persist_Security_Info, KEY.Persist_Security_Info); - hash.Add(KEY.Pooling, KEY.Pooling); - hash.Add(KEY.Replication, KEY.Replication); - hash.Add(KEY.TrustServerCertificate, KEY.TrustServerCertificate); - hash.Add(KEY.TransactionBinding, KEY.TransactionBinding); - hash.Add(KEY.Type_System_Version, KEY.Type_System_Version); - hash.Add(KEY.ColumnEncryptionSetting, KEY.ColumnEncryptionSetting); - hash.Add(KEY.EnclaveAttestationUrl, KEY.EnclaveAttestationUrl); - hash.Add(KEY.AttestationProtocol, KEY.AttestationProtocol); - hash.Add(KEY.User_ID, KEY.User_ID); - hash.Add(KEY.User_Instance, KEY.User_Instance); - hash.Add(KEY.Workstation_Id, KEY.Workstation_Id); - hash.Add(KEY.Connect_Retry_Count, KEY.Connect_Retry_Count); - hash.Add(KEY.Connect_Retry_Interval, KEY.Connect_Retry_Interval); - hash.Add(KEY.Authentication, KEY.Authentication); - hash.Add(KEY.IPAddressPreference, KEY.IPAddressPreference); + int count = SqlConnectionStringBuilder.KeywordsCount + SynonymCount; + synonyms = new Dictionary(count) + { + { KEY.ApplicationIntent, KEY.ApplicationIntent}, + { KEY.Application_Name, KEY.Application_Name }, + { KEY.AttachDBFilename, KEY.AttachDBFilename }, + { KEY.PoolBlockingPeriod, KEY.PoolBlockingPeriod }, + { KEY.Connect_Timeout, KEY.Connect_Timeout }, + { KEY.Command_Timeout, KEY.Command_Timeout }, + { KEY.Connection_Reset, KEY.Connection_Reset }, + { KEY.Context_Connection, KEY.Context_Connection }, + { KEY.Current_Language, KEY.Current_Language }, + { KEY.Data_Source, KEY.Data_Source }, + { KEY.Encrypt, KEY.Encrypt }, + { KEY.Enlist, KEY.Enlist }, + { KEY.FailoverPartner, KEY.FailoverPartner }, + { KEY.Initial_Catalog, KEY.Initial_Catalog }, + { KEY.Integrated_Security, KEY.Integrated_Security }, + { KEY.Load_Balance_Timeout, KEY.Load_Balance_Timeout }, + { KEY.MARS, KEY.MARS }, + { KEY.Max_Pool_Size, KEY.Max_Pool_Size }, + { KEY.Min_Pool_Size, KEY.Min_Pool_Size }, + { KEY.MultiSubnetFailover, KEY.MultiSubnetFailover }, + { KEY.TransparentNetworkIPResolution, KEY.TransparentNetworkIPResolution }, + { KEY.Network_Library, KEY.Network_Library }, + { KEY.Packet_Size, KEY.Packet_Size }, + { KEY.Password, KEY.Password }, + { KEY.Persist_Security_Info, KEY.Persist_Security_Info }, + { KEY.Pooling, KEY.Pooling }, + { KEY.Replication, KEY.Replication }, + { KEY.TrustServerCertificate, KEY.TrustServerCertificate }, + { KEY.TransactionBinding, KEY.TransactionBinding }, + { KEY.Type_System_Version, KEY.Type_System_Version }, + { KEY.ColumnEncryptionSetting, KEY.ColumnEncryptionSetting }, + { KEY.EnclaveAttestationUrl, KEY.EnclaveAttestationUrl }, + { KEY.AttestationProtocol, KEY.AttestationProtocol }, + { KEY.User_ID, KEY.User_ID }, + { KEY.User_Instance, KEY.User_Instance }, + { KEY.Workstation_Id, KEY.Workstation_Id }, + { KEY.Connect_Retry_Count, KEY.Connect_Retry_Count }, + { KEY.Connect_Retry_Interval, KEY.Connect_Retry_Interval }, + { KEY.Authentication, KEY.Authentication }, + { KEY.IPAddressPreference, KEY.IPAddressPreference }, #if ADONET_CERT_AUTH - hash.Add(KEY.Certificate, KEY.Certificate); + { KEY.Certificate, KEY.Certificate }, #endif - hash.Add(SYNONYM.APPLICATIONINTENT, KEY.ApplicationIntent); - hash.Add(SYNONYM.APP, KEY.Application_Name); - hash.Add(SYNONYM.EXTENDED_PROPERTIES, KEY.AttachDBFilename); - hash.Add(SYNONYM.INITIAL_FILE_NAME, KEY.AttachDBFilename); - hash.Add(SYNONYM.CONNECTION_TIMEOUT, KEY.Connect_Timeout); - hash.Add(SYNONYM.CONNECTRETRYCOUNT, KEY.Connect_Retry_Count); - hash.Add(SYNONYM.CONNECTRETRYINTERVAL, KEY.Connect_Retry_Interval); - hash.Add(SYNONYM.TIMEOUT, KEY.Connect_Timeout); - hash.Add(SYNONYM.LANGUAGE, KEY.Current_Language); - hash.Add(SYNONYM.ADDR, KEY.Data_Source); - hash.Add(SYNONYM.ADDRESS, KEY.Data_Source); - hash.Add(SYNONYM.MULTIPLEACTIVERESULTSETS, KEY.MARS); - hash.Add(SYNONYM.MULTISUBNETFAILOVER, KEY.MultiSubnetFailover); - hash.Add(SYNONYM.NETWORK_ADDRESS, KEY.Data_Source); - hash.Add(SYNONYM.SERVER, KEY.Data_Source); - hash.Add(SYNONYM.DATABASE, KEY.Initial_Catalog); - hash.Add(SYNONYM.TRUSTED_CONNECTION, KEY.Integrated_Security); - hash.Add(SYNONYM.Connection_Lifetime, KEY.Load_Balance_Timeout); - hash.Add(SYNONYM.NET, KEY.Network_Library); - hash.Add(SYNONYM.NETWORK, KEY.Network_Library); - hash.Add(SYNONYM.Pwd, KEY.Password); - hash.Add(SYNONYM.POOLBLOCKINGPERIOD, KEY.PoolBlockingPeriod); - hash.Add(SYNONYM.PERSISTSECURITYINFO, KEY.Persist_Security_Info); - hash.Add(SYNONYM.TRANSPARENTNETWORKIPRESOLUTION, KEY.TransparentNetworkIPResolution); - hash.Add(SYNONYM.TRUSTSERVERCERTIFICATE, KEY.TrustServerCertificate); - hash.Add(SYNONYM.UID, KEY.User_ID); - hash.Add(SYNONYM.User, KEY.User_ID); - hash.Add(SYNONYM.WSID, KEY.Workstation_Id); - hash.Add(SYNONYM.IPADDRESSPREFERENCE, KEY.IPAddressPreference); - Debug.Assert(SqlConnectionStringBuilder.KeywordsCount + SynonymCount == hash.Count, "incorrect initial ParseSynonyms size"); - _sqlClientSynonyms = hash; - } - return hash; + { SYNONYM.APPLICATIONINTENT, KEY.ApplicationIntent }, + { SYNONYM.APP, KEY.Application_Name }, + { SYNONYM.EXTENDED_PROPERTIES, KEY.AttachDBFilename }, + { SYNONYM.INITIAL_FILE_NAME, KEY.AttachDBFilename }, + { SYNONYM.CONNECTION_TIMEOUT, KEY.Connect_Timeout }, + { SYNONYM.CONNECTRETRYCOUNT, KEY.Connect_Retry_Count }, + { SYNONYM.CONNECTRETRYINTERVAL, KEY.Connect_Retry_Interval }, + { SYNONYM.TIMEOUT, KEY.Connect_Timeout }, + { SYNONYM.LANGUAGE, KEY.Current_Language }, + { SYNONYM.ADDR, KEY.Data_Source }, + { SYNONYM.ADDRESS, KEY.Data_Source }, + { SYNONYM.MULTIPLEACTIVERESULTSETS, KEY.MARS }, + { SYNONYM.MULTISUBNETFAILOVER, KEY.MultiSubnetFailover }, + { SYNONYM.NETWORK_ADDRESS, KEY.Data_Source }, + { SYNONYM.SERVER, KEY.Data_Source }, + { SYNONYM.DATABASE, KEY.Initial_Catalog }, + { SYNONYM.TRUSTED_CONNECTION, KEY.Integrated_Security }, + { SYNONYM.Connection_Lifetime, KEY.Load_Balance_Timeout }, + { SYNONYM.NET, KEY.Network_Library }, + { SYNONYM.NETWORK, KEY.Network_Library }, + { SYNONYM.Pwd, KEY.Password }, + { SYNONYM.POOLBLOCKINGPERIOD, KEY.PoolBlockingPeriod }, + { SYNONYM.PERSISTSECURITYINFO, KEY.Persist_Security_Info }, + { SYNONYM.TRANSPARENTNETWORKIPRESOLUTION, KEY.TransparentNetworkIPResolution }, + { SYNONYM.TRUSTSERVERCERTIFICATE, KEY.TrustServerCertificate }, + { SYNONYM.UID, KEY.User_ID }, + { SYNONYM.User, KEY.User_ID }, + { SYNONYM.WSID, KEY.Workstation_Id }, + { SYNONYM.IPADDRESSPREFERENCE, KEY.IPAddressPreference } + }; + Debug.Assert(count == synonyms.Count, "incorrect initial ParseSynonyms size"); + s_sqlClientSynonyms = synonyms; + } + return synonyms; } internal string ObtainWorkstationId() @@ -964,8 +967,8 @@ internal static void VerifyLocalHostAndFixup(ref string host, bool enforceLocalH internal Microsoft.Data.SqlClient.ApplicationIntent ConvertValueToApplicationIntent() { - object value = base.Parsetable[KEY.ApplicationIntent]; - if (value == null) + string value; + if (!base.Parsetable.TryGetValue(KEY.ApplicationIntent, out value)) { return DEFAULT.ApplicationIntent; } @@ -989,8 +992,8 @@ internal Microsoft.Data.SqlClient.ApplicationIntent ConvertValueToApplicationInt internal Microsoft.Data.SqlClient.PoolBlockingPeriod ConvertValueToPoolBlockingPeriod() { - object value = base.Parsetable[KEY.PoolBlockingPeriod]; - if (value == null) + string value; + if (!base.Parsetable.TryGetValue(KEY.PoolBlockingPeriod, out value)) { return DEFAULT.PoolBlockingPeriod; } @@ -1011,10 +1014,8 @@ internal Microsoft.Data.SqlClient.PoolBlockingPeriod ConvertValueToPoolBlockingP internal SqlAuthenticationMethod ConvertValueToAuthenticationType() { - object value = base.Parsetable[KEY.Authentication]; - - string valStr = value as string; - if (valStr == null) + string valStr; + if (!base.Parsetable.TryGetValue(KEY.Authentication, out valStr)) { return DEFAULT.Authentication; } @@ -1039,10 +1040,8 @@ internal SqlAuthenticationMethod ConvertValueToAuthenticationType() /// internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSetting() { - object value = base.Parsetable[KEY.ColumnEncryptionSetting]; - - string valStr = value as string; - if (valStr == null) + string valStr; + if (!base.Parsetable.TryGetValue(KEY.ColumnEncryptionSetting, out valStr)) { return DEFAULT.ColumnEncryptionSetting; } @@ -1063,10 +1062,8 @@ internal SqlConnectionColumnEncryptionSetting ConvertValueToColumnEncryptionSett internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() { - object value = base.Parsetable[KEY.AttestationProtocol]; - - string valStr = value as string; - if (valStr == null) + string valStr; + if (!base.Parsetable.TryGetValue(KEY.AttestationProtocol, out valStr)) { return DEFAULT.AttestationProtocol; } @@ -1091,10 +1088,8 @@ internal SqlConnectionAttestationProtocol ConvertValueToAttestationProtocol() /// internal SqlConnectionIPAddressPreference ConvertValueToIPAddressPreference() { - object value = base.Parsetable[KEY.IPAddressPreference]; - - string valStr = value as string; - if (valStr == null) + string valStr; + if (!base.Parsetable.TryGetValue(KEY.IPAddressPreference, out valStr)) { return DEFAULT.s_IPAddressPreference; } @@ -1115,10 +1110,7 @@ internal SqlConnectionIPAddressPreference ConvertValueToIPAddressPreference() internal bool ConvertValueToEncrypt() { - // If the Authentication keyword is provided, default to Encrypt=true; - // otherwise keep old default for backwards compatibility - object authValue = base.Parsetable[KEY.Authentication]; - bool defaultEncryptValue = (authValue == null) ? DEFAULT.Encrypt : true; + bool defaultEncryptValue = !base.Parsetable.ContainsKey(KEY.Authentication) ? DEFAULT.Encrypt : true; return ConvertValueToBoolean(KEY.Encrypt, defaultEncryptValue); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 13f0454c8c..c6844a0568 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -1987,7 +1987,7 @@ private bool ShouldDisableTnir(SqlConnectionString connectionOptions) // Check if the user had explicitly specified the TNIR option in the connection string or the connection string builder. // If the user has specified the option in the connection string explicitly, then we shouldn't disable TNIR. - bool isTnirExplicitlySpecifiedInConnectionOptions = connectionOptions.Parsetable[SqlConnectionString.KEY.TransparentNetworkIPResolution] != null; + bool isTnirExplicitlySpecifiedInConnectionOptions = connectionOptions.Parsetable.ContainsKey(SqlConnectionString.KEY.TransparentNetworkIPResolution); return isTnirExplicitlySpecifiedInConnectionOptions ? false : (isAzureEndPoint || isFedAuthEnabled); } @@ -2566,7 +2566,7 @@ internal void OnLoginAck(SqlLoginAck rec) /// Federated Authentication Info. internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert((ConnectionOptions.HasUserIdKeyword && ConnectionOptions.HasPasswordKeyword) + Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) || _credential != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs similarity index 83% rename from src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs index 50b1c83559..de5d1cb9e5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs @@ -14,6 +14,26 @@ namespace Microsoft.Data.Common { partial class DbConnectionOptions { + // instances of this class are intended to be immutable, i.e readonly + // used by pooling classes so it is much easier to verify correctness + // when not worried about the class being modified during execution + + // connection string common keywords + private static class KEY + { + internal const string Integrated_Security = DbConnectionStringKeywords.IntegratedSecurity; + internal const string Password = DbConnectionStringKeywords.Password; + internal const string Persist_Security_Info = DbConnectionStringKeywords.PersistSecurityInfo; + internal const string User_ID = DbConnectionStringKeywords.UserID; + } + + // known connection string common synonyms + private static class SYNONYM + { + internal const string Pwd = DbConnectionStringSynonyms.Pwd; + internal const string UID = DbConnectionStringSynonyms.UID; + } + #if DEBUG /*private const string ConnectionStringPatternV1 = "[\\s;]*" @@ -79,52 +99,115 @@ partial class DbConnectionOptions private static readonly Regex s_connectionStringQuoteValueRegex = new Regex(ConnectionStringQuoteValuePattern, RegexOptions.Compiled); private static readonly Regex s_connectionStringQuoteOdbcValueRegex = new Regex(ConnectionStringQuoteOdbcValuePattern, RegexOptions.ExplicitCapture | RegexOptions.Compiled); - // connection string common keywords - private static class KEY + internal readonly bool _hasPasswordKeyword; + internal readonly bool _hasUserIdKeyword; + internal readonly NameValuePair _keyChain; + + private readonly string _usersConnectionString; + private readonly Dictionary _parsetable; + + internal Dictionary Parsetable => _parsetable; + public bool IsEmpty => _keyChain == null; + + public DbConnectionOptions(string connectionString, Dictionary synonyms) { - internal const string Integrated_Security = "integrated security"; - internal const string Password = "password"; - internal const string Persist_Security_Info = "persist security info"; - internal const string User_ID = "user id"; - internal const string AttachDBFileName = "attachdbfilename"; + _parsetable = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + _usersConnectionString = ((null != connectionString) ? connectionString : ""); + + // first pass on parsing, initial syntax check + if (0 < _usersConnectionString.Length) + { + _keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, false); + _hasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd)); + _hasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID)); + } } - // known connection string common synonyms - private static class SYNONYM + protected DbConnectionOptions(DbConnectionOptions connectionOptions) + { // Clone used by SqlConnectionString + _usersConnectionString = connectionOptions._usersConnectionString; + _parsetable = connectionOptions._parsetable; + _keyChain = connectionOptions._keyChain; + _hasPasswordKeyword = connectionOptions._hasPasswordKeyword; + _hasUserIdKeyword = connectionOptions._hasUserIdKeyword; + } + + internal bool TryGetParsetableValue(string key, out string value) => _parsetable.TryGetValue(key, out value); + + // same as Boolean, but with SSPI thrown in as valid yes + public bool ConvertValueToIntegratedSecurity() { - internal const string Pwd = "pwd"; - internal const string UID = "uid"; + return _parsetable.TryGetValue(KEY.Integrated_Security, out string value) && value != null ? + ConvertValueToIntegratedSecurityInternal(value) : + false; } - internal readonly bool HasPasswordKeyword; - internal readonly bool HasUserIdKeyword; + internal bool ConvertValueToIntegratedSecurityInternal(string stringValue) + { + if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes")) + return true; + else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no")) + return false; + else + { + string tmp = stringValue.Trim(); // Remove leading & trailing whitespace. + if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes")) + return true; + else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no")) + return false; + else + { + throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security); + } + } + } - private readonly string _usersConnectionString; - private readonly Dictionary _parsetable; - internal readonly NameValuePair _keyChain; + public int ConvertValueToInt32(string keyName, int defaultValue) + { + return _parsetable.TryGetValue(keyName, out string value) && value != null ? + ConvertToInt32Internal(keyName, value) : + defaultValue; + } - internal Dictionary Parsetable + internal static int ConvertToInt32Internal(string keyname, string stringValue) { - get { return _parsetable; } + try + { + return int.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(keyname, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(keyname, e); + } } - public string UsersConnectionString(bool hidePassword) => - UsersConnectionString(hidePassword, false); + public string ConvertValueToString(string keyName, string defaultValue) + => _parsetable.TryGetValue(keyName, out string value) && value != null ? value : defaultValue; + + public bool ContainsKey(string keyword) => _parsetable.ContainsKey(keyword); + + protected internal virtual string Expand() => _usersConnectionString; + + public string UsersConnectionString(bool hidePassword) => UsersConnectionString(hidePassword, false); internal string UsersConnectionStringForTrace() => UsersConnectionString(true, true); private string UsersConnectionString(bool hidePassword, bool forceHidePassword) { string connectionString = _usersConnectionString; - if (HasPasswordKeyword && (forceHidePassword || (hidePassword && !HasPersistablePassword))) + if (_hasPasswordKeyword && (forceHidePassword || (hidePassword && !HasPersistablePassword))) { ReplacePasswordPwd(out connectionString, false); } return connectionString ?? string.Empty; } - internal bool HasPersistablePassword => HasPasswordKeyword ? - ConvertValueToBoolean(KEY.Persist_Security_Info, false) : + internal bool HasPersistablePassword => _hasPasswordKeyword ? + ConvertValueToBoolean(KEY.Persist_Security_Info, DbConnectionStringDefaults.PersistSecurityInfo) : true; // no password means persistable password so we don't have to munge public bool ConvertValueToBoolean(string keyName, bool defaultValue) @@ -155,8 +238,8 @@ internal static bool ConvertValueToBooleanInternal(string keyName, string string } } - private static bool CompareInsensitiveInvariant(string strvalue, string strconst) => - (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst)); + private static bool CompareInsensitiveInvariant(string strvalue, string strconst) + => (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst)); [System.Diagnostics.Conditional("DEBUG")] private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dictionary synonyms) @@ -164,9 +247,10 @@ private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Dict if (SqlClientEventSource.Log.IsAdvancedTraceOn()) { Debug.Assert(string.Equals(keyname, keyname?.ToLower(), StringComparison.InvariantCulture), "missing ToLower"); - string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); + string realkeyname = ((null != synonyms) ? synonyms[keyname] : keyname); - if ((KEY.Password != realkeyname) && (SYNONYM.Pwd != realkeyname)) + if (!string.Equals(KEY.Password, realkeyname, StringComparison.InvariantCultureIgnoreCase) && + !string.Equals(SYNONYM.Pwd, realkeyname, StringComparison.InvariantCultureIgnoreCase)) { // don't trace passwords ever! if (null != keyvalue) @@ -450,7 +534,7 @@ private static bool IsKeyNameValid(string keyname) { #if DEBUG bool compValue = s_connectionStringValidKeyRegex.IsMatch(keyname); - Debug.Assert(((0 < keyname.Length) && (';' != keyname[0]) && !Char.IsWhiteSpace(keyname[0]) && (-1 == keyname.IndexOf('\u0000'))) == compValue, "IsValueValid mismatch with regex"); + Debug.Assert(((0 < keyname.Length) && (';' != keyname[0]) && !char.IsWhiteSpace(keyname[0]) && (-1 == keyname.IndexOf('\u0000'))) == compValue, "IsValueValid mismatch with regex"); #endif return ((0 < keyname.Length) && (';' != keyname[0]) && !char.IsWhiteSpace(keyname[0]) && (-1 == keyname.IndexOf('\u0000'))); } @@ -595,14 +679,14 @@ private static NameValuePair ParseInternal(Dictionary parsetable } #if DEBUG DebugTraceKeyValuePair(keyname, keyvalue, synonyms); - +#endif Debug.Assert(IsKeyNameValid(keyname), "ParseFailure, invalid keyname"); Debug.Assert(IsValueValidInternal(keyvalue), "parse failure, invalid keyvalue"); -#endif - string synonym; - string realkeyname = null != synonyms ? - (synonyms.TryGetValue(keyname, out synonym) ? synonym : null) : - keyname; + + string realkeyname = (synonyms is not null) ? + (synonyms.TryGetValue(keyname, out string synonym) ? synonym : null) : + keyname; + if (!IsKeyNameValid(realkeyname)) { throw ADP.KeywordNotSupported(keyname); @@ -641,7 +725,8 @@ internal NameValuePair ReplacePasswordPwd(out string constr, bool fakePassword) StringBuilder builder = new StringBuilder(_usersConnectionString.Length); for (NameValuePair current = _keyChain; null != current; current = current.Next) { - if ((KEY.Password != current.Name) && (SYNONYM.Pwd != current.Name)) + if(!string.Equals(KEY.Password, current.Name, StringComparison.InvariantCultureIgnoreCase) && + !string.Equals(SYNONYM.Pwd, current.Name, StringComparison.InvariantCultureIgnoreCase)) { builder.Append(_usersConnectionString, copyPosition, current.Length); if (fakePassword)