diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs index a136ba2642..3d5d6ecf85 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs @@ -392,16 +392,15 @@ void Cancel() Socket availableSocket = null; try { - int n = 0; // Socket index - // We go through the IP list twice. // In the first traversal, we only try to connect with the preferedIPFamilies[0]. // In the second traversal, we only try to connect with the preferedIPFamilies[1]. // For UsePlatformDefault preference, we do traversal once. for (int i = 0; i < preferedIPFamilies.Length; ++i) { - foreach (IPAddress ipAddress in ipAddresses) + for (int n = 0; n < ipAddresses.Length; n++) { + IPAddress ipAddress = ipAddresses[n]; try { if (ipAddress != null) @@ -443,7 +442,6 @@ void Cancel() sockets[n] = null; } } - n++; } } catch (Exception e) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs index 54561e1be9..f53000e61c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/ConnectionHelper.cs @@ -17,6 +17,7 @@ internal static class ConnectionHelper private static Type s_dbConnectionInternal = s_MicrosoftDotData.GetType("Microsoft.Data.ProviderBase.DbConnectionInternal"); private static Type s_tdsParser = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.TdsParser"); private static Type s_tdsParserStateObject = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.TdsParserStateObject"); + private static Type s_SQLDNSInfo = s_MicrosoftDotData.GetType("Microsoft.Data.SqlClient.SQLDNSInfo"); private static PropertyInfo s_sqlConnectionInternalConnection = s_sqlConnection.GetProperty("InnerConnection", BindingFlags.Instance | BindingFlags.NonPublic); private static PropertyInfo s_dbConnectionInternalPool = s_dbConnectionInternal.GetProperty("Pool", BindingFlags.Instance | BindingFlags.NonPublic); private static MethodInfo s_dbConnectionInternalIsConnectionAlive = s_dbConnectionInternal.GetMethod("IsConnectionAlive", BindingFlags.Instance | BindingFlags.NonPublic); @@ -26,6 +27,11 @@ internal static class ConnectionHelper private static FieldInfo s_tdsParserStateObjectProperty = s_tdsParser.GetField("_physicalStateObj", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo s_enforceTimeoutDelayProperty = s_tdsParserStateObject.GetField("_enforceTimeoutDelay", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo s_enforcedTimeoutDelayInMilliSeconds = s_tdsParserStateObject.GetField("_enforcedTimeoutDelayInMilliSeconds", BindingFlags.Instance | BindingFlags.NonPublic); + private static FieldInfo s_pendingSQLDNSObject = s_sqlInternalConnectionTds.GetField("pendingSQLDNSObject", BindingFlags.Instance | BindingFlags.NonPublic); + private static PropertyInfo s_pendingSQLDNS_FQDN = s_SQLDNSInfo.GetProperty("FQDN", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_AddrIPv4 = s_SQLDNSInfo.GetProperty("AddrIPv4", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_AddrIPv6 = s_SQLDNSInfo.GetProperty("AddrIPv6", BindingFlags.Instance | BindingFlags.Public); + private static PropertyInfo s_pendingSQLDNS_Port = s_SQLDNSInfo.GetProperty("Port", BindingFlags.Instance | BindingFlags.Public); public static object GetConnectionPool(object internalConnection) { @@ -79,5 +85,17 @@ public static void SetEnforcedTimeout(this SqlConnection connection, bool enforc s_enforceTimeoutDelayProperty.SetValue(stateObj, enforce); s_enforcedTimeoutDelayInMilliSeconds.SetValue(stateObj, timeout); } + + public static Tuple GetSQLDNSInfo(this SqlConnection connection) + { + object internalConnection = GetInternalConnection(connection); + VerifyObjectIsInternalConnection(internalConnection); + object pendingSQLDNSInfo = s_pendingSQLDNSObject.GetValue(internalConnection); + string fqdn = s_pendingSQLDNS_FQDN.GetValue(pendingSQLDNSInfo) as string; + string ipv4 = s_pendingSQLDNS_AddrIPv4.GetValue(pendingSQLDNSInfo) as string; + string ipv6 = s_pendingSQLDNS_AddrIPv6.GetValue(pendingSQLDNSInfo) as string; + string port = s_pendingSQLDNS_Port.GetValue(pendingSQLDNSInfo) as string; + return new Tuple(fqdn, ipv4, ipv6, port); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs index fa49119817..50e604bcca 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConfigurableIpPreferenceTest/ConfigurableIpPreferenceTest.cs @@ -3,7 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Reflection; +using Microsoft.Data.SqlClient.ManualTesting.Tests.SystemDataInternals; using Xunit; using static Microsoft.Data.SqlClient.ManualTesting.Tests.DataTestUtility; @@ -16,19 +22,70 @@ public class ConfigurableIpPreferenceTest private const string CnnPrefIPv6 = ";IPAddressPreference=IPv6First"; private const string CnnPrefIPv4 = ";IPAddressPreference=IPv4First"; + private static bool IsTCPConnectionStringSetup() => !string.IsNullOrEmpty(TCPConnectionString); + private static bool IsValidDataSource() + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(TCPConnectionString); + int startIdx = builder.DataSource.IndexOf(':') + 1; + int endIdx = builder.DataSource.IndexOf(','); + string serverName; + if (endIdx == -1) + { + serverName = builder.DataSource.Substring(startIdx); + } + else + { + serverName = builder.DataSource.Substring(startIdx, endIdx - startIdx); + } + + List ipAddresses = Dns.GetHostAddresses(serverName).ToList(); + return ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetwork) && + ipAddresses.Exists(ip => ip.AddressFamily == AddressFamily.InterNetworkV6); + } + + [ConditionalTheory(nameof(IsTCPConnectionStringSetup), nameof(IsValidDataSource))] + [InlineData(CnnPrefIPv6)] + [InlineData(CnnPrefIPv4)] + [InlineData(";IPAddressPreference=UsePlatformDefault")] + public void ConfigurableIpPreference(string ipPreference) + { + using (SqlConnection connection = new SqlConnection(TCPConnectionString + ipPreference)) + { + connection.Open(); + Assert.Equal(ConnectionState.Open, connection.State); +#if !NETFRAMEWORK + Tuple DNSInfo = connection.GetSQLDNSInfo(); + if(ipPreference == CnnPrefIPv4) + { + Assert.NotNull(DNSInfo.Item2); //IPv4 + Assert.Null(DNSInfo.Item3); //IPv6 + } + else if(ipPreference == CnnPrefIPv6) + { + Assert.Null(DNSInfo.Item2); + Assert.NotNull(DNSInfo.Item3); + } + else + { + Assert.True((DNSInfo.Item2 != null && DNSInfo.Item3 == null) || (DNSInfo.Item2 == null && DNSInfo.Item3 != null)); + } +#endif + } + } + [ConditionalTheory(typeof(DataTestUtility), nameof(DoesHostAddressContainBothIPv4AndIPv6))] [InlineData(CnnPrefIPv6)] [InlineData(CnnPrefIPv4)] public void ConfigurableIpPreferenceManagedSni(string ipPreference) { AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true); - TestConfigurableIpPreference(ipPreference); + TestCachedConfigurableIpPreference(ipPreference, DNSCachingConnString); AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", false); } - private void TestConfigurableIpPreference(string ipPreference) + private void TestCachedConfigurableIpPreference(string ipPreference, string cnnString) { - using (SqlConnection connection = new SqlConnection(DNSCachingConnString + ipPreference)) + using (SqlConnection connection = new SqlConnection(cnnString + ipPreference)) { // each successful connection updates the dns cache entry for the data source connection.Open(); @@ -43,6 +100,7 @@ private void TestConfigurableIpPreference(string ipPreference) const string AddrIPv6Property = "AddrIPv6"; const string FQDNProperty = "FQDN"; + Assert.NotNull(dnsCacheEntry); Assert.Equal(connection.DataSource, GetPropertyValueFromCacheEntry(FQDNProperty, dnsCacheEntry)); if (ipPreference == CnnPrefIPv4) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs index 33460acb8d..a23efff073 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Reflection; using Xunit;