From b275dd0829586c5efac32d50402306488c4336a3 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 5 Jun 2020 18:29:49 -0700 Subject: [PATCH 1/9] Add DNS Caching phase 1 support for NetFx and NetCore --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 12 ++ .../Interop/SNINativeMethodWrapper.Windows.cs | 75 ++++++++- .../src/Microsoft.Data.SqlClient.csproj | 3 + .../Microsoft/Data/SqlClient/SNI/SNIProxy.cs | 12 +- .../Data/SqlClient/SNI/SNITcpHandle.cs | 152 +++++++++++++++--- .../Microsoft/Data/SqlClient/SqlConnection.cs | 46 ++++++ .../SqlClient/SqlInternalConnectionTds.cs | 97 ++++++++++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 2 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 71 +++++++- .../Data/SqlClient/TdsParserSafeHandles.cs | 9 +- .../Data/SqlClient/TdsParserStateObject.cs | 4 +- .../SqlClient/TdsParserStateObjectManaged.cs | 10 +- .../SqlClient/TdsParserStateObjectNative.cs | 65 +++++++- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + .../Interop/SNINativeManagedWrapperX64.cs | 15 +- .../Interop/SNINativeManagedWrapperX86.cs | 15 +- .../Data/Interop/SNINativeMethodWrapper.cs | 93 ++++++++++- .../Microsoft/Data/SqlClient/SqlConnection.cs | 48 ++++++ .../SqlClient/SqlInternalConnectionTds.cs | 97 ++++++++++- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 4 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 117 +++++++++++++- .../Data/SqlClient/TdsParserSafeHandles.cs | 9 +- .../Data/SqlClient/TdsParserStateObject.cs | 16 +- .../Microsoft/Data/SqlClient/SQLDNSCache.cs | 87 ++++++++++ 24 files changed, 999 insertions(+), 63 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index eb88922f7a..38857196bf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -515,6 +515,18 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public System.Guid ClientConnectionId { get { throw null; } } + + /// + /// for internal test only + /// + [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] + internal string SQLDNSCachingSupportedState { get { throw null; } } + /// + /// for internal test only + /// + [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] + internal string SQLDNSCachingSupportedStateBeforeRedirect { get { throw null; } } + object System.ICloneable.Clone() { throw null; } /// [System.ComponentModel.DefaultValueAttribute("")] diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index 1296e4afa3..795c14c5b1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -6,6 +6,7 @@ using Microsoft.Data.SqlClient; using System; using System.Runtime.InteropServices; +using System.Text; namespace Microsoft.Data.SqlClient { @@ -20,6 +21,8 @@ internal static partial class SNINativeMethodWrapper [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void SqlAsyncCallbackDelegate(IntPtr m_ConsKey, IntPtr pPacket, uint dwError); + internal const int SniIP6AddrStringBufferLength = 48; // from SNI layer + internal static int SniMaxComposedSpnLength { get @@ -162,6 +165,20 @@ private unsafe struct SNI_CLIENT_CONSUMER_INFO public TransparentNetworkResolutionMode transparentNetworkResolution; public int totalTimeout; public bool isAzureSqlServerEndpoint; + public SNI_DNSCache_Info DNSCacheInfo; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct SNI_DNSCache_Info + { + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedFQDN; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpIPv4; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpIPv6; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpPort; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] @@ -236,6 +253,17 @@ internal struct SNI_Error [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out Guid pbQInfo); + // kz start + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + private static extern uint SNIGetPeerAddrStrWrapper([In] SNIHandle pConn, int bufferSize, StringBuilder addrBuffer, out uint addrLen); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); + // kz end + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIInitialize([In] IntPtr pmo); @@ -248,7 +276,8 @@ private static extern uint SNIOpenWrapper( [MarshalAs(UnmanagedType.LPWStr)] string szConnect, [In] SNIHandle pConn, out IntPtr ppConn, - [MarshalAs(UnmanagedType.Bool)] bool fSync); + [MarshalAs(UnmanagedType.Bool)] bool fSync, + [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr SNIPacketAllocateWrapper([In] SafeHandle pConn, IOType IOType); @@ -283,22 +312,55 @@ internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } + + // kz start + internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) + { + return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); + } + + internal static uint SniGetConnectionPort(SNIHandle pConn, ref ushort portNum) + { + return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PEERPORT, out portNum); + } + + internal static uint SniGetConnectionIPString(SNIHandle pConn, ref string connIPStr) + { + UInt32 ret; + uint connIPLen = 0; + + int bufferSize = SniIP6AddrStringBufferLength; + StringBuilder addrBuffer = new StringBuilder(bufferSize); + + ret = SNIGetPeerAddrStrWrapper(pConn, bufferSize, addrBuffer, out connIPLen); + + connIPStr = addrBuffer.ToString(0, Convert.ToInt32(connIPLen)); + + return ret; + } + // kz end internal static uint SNIInitialize() { return SNIInitialize(IntPtr.Zero); } - internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync) + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS Sni_Consumer_Info native_consumerInfo = new Sni_Consumer_Info(); MarshalConsumerInfo(consumerInfo, ref native_consumerInfo); - return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync); + SNI_DNSCache_Info native_cachedDNSInfo = new SNI_DNSCache_Info(); + native_cachedDNSInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; + native_cachedDNSInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; + native_cachedDNSInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; + native_cachedDNSInfo.wszCachedTcpPort = cachedDNSInfo?.Port; + + return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel) + internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, SQLDNSInfo cachedDNSInfo) { fixed (byte* pin_instanceName = &instanceName[0]) { @@ -321,6 +383,11 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons clientConsumerInfo.totalTimeout = SniOpenTimeOut; clientConsumerInfo.isAzureSqlServerEndpoint = ADP.IsAzureSqlServerEndpoint(constring); + clientConsumerInfo.DNSCacheInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpPort = cachedDNSInfo?.Port; + if (spnBuffer != null) { fixed (byte* pin_spnBuffer = &spnBuffer[0]) 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 b2d8e6830a..1b9b7a79df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -138,6 +138,9 @@ Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + + Microsoft\Data\SqlClient\SQLDNSCache.cs + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs index dd26939d29..eaeedf8f20 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs @@ -263,8 +263,10 @@ public uint WritePacket(SNIHandle handle, SNIPacket packet, bool sync) /// Asynchronous connection /// Attempt parallel connects /// + /// Used for DNS Cache + /// Used for DNS Cache /// SNI handle - public SNIHandle CreateConnectionHandle(object callbackObject, string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity) + public SNIHandle CreateConnectionHandle(object callbackObject, string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { instanceName = new byte[1]; @@ -291,7 +293,7 @@ public SNIHandle CreateConnectionHandle(object callbackObject, string fullServer case DataSource.Protocol.Admin: case DataSource.Protocol.None: // default to using tcp if no protocol is provided case DataSource.Protocol.TCP: - sniHandle = CreateTcpHandle(details, timerExpire, callbackObject, parallel); + sniHandle = CreateTcpHandle(details, timerExpire, callbackObject, parallel, cachedFQDN, ref pendingDNSInfo); break; case DataSource.Protocol.NP: sniHandle = CreateNpHandle(details, timerExpire, callbackObject, parallel); @@ -373,8 +375,10 @@ private static byte[] GetSqlServerSPN(string hostNameOrAddress, string portOrIns /// Timer expiration /// Asynchronous I/O callback object /// Should MultiSubnetFailover be used + /// Key for DNS Cache + /// Used for DNS Cache /// SNITCPHandle - private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, object callbackObject, bool parallel) + private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, object callbackObject, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { // TCP Format: // tcp:\ @@ -412,7 +416,7 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, objec port = isAdminConnection ? DefaultSqlServerDacPort : DefaultSqlServerPort; } - return new SNITCPHandle(hostName, port, timerExpire, callbackObject, parallel); + return new SNITCPHandle(hostName, port, timerExpire, callbackObject, parallel, cachedFQDN, ref pendingDNSInfo); } 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 158c13949a..32280880eb 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 @@ -101,12 +101,17 @@ public override uint Status /// Connection timer expiration /// Callback object /// Parallel executions - public SNITCPHandle(string serverName, int port, long timerExpire, object callbackObject, bool parallel) + /// Key for DNS Cache + /// Used for DNS Cache + public SNITCPHandle(string serverName, int port, long timerExpire, object callbackObject, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { _callbackObject = callbackObject; _targetServer = serverName; _sendSync = new object(); + SQLDNSInfo cachedDNSInfo; + bool hasCachedDNSInfo = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + try { TimeSpan ts = default(TimeSpan); @@ -120,33 +125,68 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; } - Task connectTask; - if (parallel) - { - Task serverAddrTask = Dns.GetHostAddressesAsync(serverName); - serverAddrTask.Wait(ts); - IPAddress[] serverAddresses = serverAddrTask.Result; + bool reportError = true; - if (serverAddresses.Length > MaxParallelIpAddresses) + try + { + if (parallel) { - // Fail if above 64 to match legacy behavior - ReportTcpSNIError(0, SNICommon.MultiSubnetFailoverWithMoreThan64IPs, string.Empty); - return; + _socket = TryConnectParallel(serverName, port, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); } - - connectTask = ParallelConnectAsync(serverAddresses, port); - - if (!(isInfiniteTimeOut ? connectTask.Wait(-1) : connectTask.Wait(ts))) + else { - ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); - return; + _socket = Connect(serverName, port, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); } - - _socket = connectTask.Result; } - else + catch (Exception ex) { - _socket = Connect(serverName, port, ts, isInfiniteTimeOut); + // Retry with cached IP address + if (ex is SocketException || ex is ArgumentException || ex is AggregateException) + { + if (hasCachedDNSInfo == false) + { + throw; + } + else + { + int portRetry = String.IsNullOrEmpty(cachedDNSInfo.Port) ? port : Int32.Parse(cachedDNSInfo.Port); + + try + { + if (parallel) + { + _socket = TryConnectParallel(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + } + else + { + _socket = Connect(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + } + } + catch(Exception exRetry) + { + if (exRetry is SocketException || exRetry is ArgumentNullException + || exRetry is ArgumentException || exRetry is ArgumentOutOfRangeException || exRetry is AggregateException) + { + if (parallel) + { + _socket = TryConnectParallel(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + } + else + { + _socket = Connect(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + } + } + else + { + throw; + } + } + } + } + else + { + throw; + } } if (_socket == null || !_socket.Connected) @@ -156,7 +196,11 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba _socket.Dispose(); _socket = null; } - ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + + if (reportError) + { + ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + } return; } @@ -181,9 +225,64 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba _status = TdsEnums.SNI_SUCCESS; } - private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout) + private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool isInfiniteTimeOut, ref bool callerReportError, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + { + Socket availableSocket = null; + Task connectTask; + + Task serverAddrTask = Dns.GetHostAddressesAsync(hostName); + serverAddrTask.Wait(ts); + IPAddress[] serverAddresses = serverAddrTask.Result; + + if (serverAddresses.Length > MaxParallelIpAddresses) + { + // Fail if above 64 to match legacy behavior + callerReportError = false; + ReportTcpSNIError(0, SNICommon.MultiSubnetFailoverWithMoreThan64IPs, string.Empty); + return availableSocket; + } + + string IPv4String = null; + string IPv6String = null; + + foreach (IPAddress ipAddress in serverAddresses) + { + if (ipAddress.AddressFamily == AddressFamily.InterNetwork) + { + IPv4String = ipAddress.ToString(); + } + else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + IPv6String = ipAddress.ToString(); + } + } + + if (IPv4String != null || IPv6String != null) + { + pendingDNSInfo = new SQLDNSInfo(cachedFQDN, IPv4String, IPv6String, port.ToString()); + } + + connectTask = ParallelConnectAsync(serverAddresses, port); + + if (!(isInfiniteTimeOut ? connectTask.Wait(-1) : connectTask.Wait(ts))) + { + callerReportError = false; + ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + return availableSocket; + } + + availableSocket = connectTask.Result; + return availableSocket; + + } + + private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); + + string IPv4String = null; + string IPv6String = null; + IPAddress serverIPv4 = null; IPAddress serverIPv6 = null; foreach (IPAddress ipAddress in ipAddresses) @@ -191,15 +290,22 @@ private static Socket Connect(string serverName, int port, TimeSpan timeout, boo if (ipAddress.AddressFamily == AddressFamily.InterNetwork) { serverIPv4 = ipAddress; + IPv4String = ipAddress.ToString(); } else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { serverIPv6 = ipAddress; + IPv6String = ipAddress.ToString(); } } ipAddresses = new IPAddress[] { serverIPv4, serverIPv6 }; Socket[] sockets = new Socket[2]; + if (IPv4String != null || IPv6String != null) + { + pendingDNSInfo = new SQLDNSInfo(cachedFQDN, IPv4String, IPv6String, port.ToString()); + } + CancellationTokenSource cts = null; void Cancel() 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 344702dae2..a80ecd6b70 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 @@ -514,6 +514,52 @@ public override string Database } } + /// + /// for internal use only + /// + internal string SQLDNSCachingSupportedState + { + get + { + SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds); + string result; + + if (null != innerConnection) + { + result = innerConnection.IsSQLDNSCachingSupported ? "true": "false"; + } + else + { + result = "innerConnection is null!"; + } + + return result; + } + } + + /// + /// for internal use only + /// + internal string SQLDNSCachingSupportedStateBeforeRedirect + { + get + { + SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds); + string result; + + if (null != innerConnection) + { + result = innerConnection.IsDNSCachingBeforeRedirectSupported ? "true": "false"; + } + else + { + result = "innerConnection is null!"; + } + + return result; + } + } + /// public override string DataSource { 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 4f69dba320..932c991887 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 @@ -128,6 +128,61 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa private readonly ActiveDirectoryAuthenticationTimeoutRetryHelper _activeDirectoryAuthTimeoutRetryHelper; private readonly SqlAuthenticationProviderManager _sqlAuthenticationProviderManager; + internal bool _cleanSQLDNSCaching = false; + + private bool _serverSupportsDNSCaching = false; + + /// + /// Get or set if SQLDNSCaching is supported by the server. + /// + internal bool IsSQLDNSCachingSupported + { + get + { + return _serverSupportsDNSCaching; + } + set + { + _serverSupportsDNSCaching = value; + } + } + + private bool _SQLDNSRetryEnabled = false; + + /// + /// Get or set if we need retrying with IP received from FeatureExtAck. + /// + internal bool IsSQLDNSRetryEnabled + { + get + { + return _SQLDNSRetryEnabled; + } + set + { + _SQLDNSRetryEnabled = value; + } + } + + private bool _DNSCachingBeforeRedirect = false; + + /// + /// Get or set if the control ring send redirect token and feature ext ack with true for DNSCaching + /// + internal bool IsDNSCachingBeforeRedirectSupported + { + get + { + return _DNSCachingBeforeRedirect; + } + set + { + _DNSCachingBeforeRedirect = value; + } + } + + internal SQLDNSInfo pendingSQLDNSObject = null; + // TCE flags internal byte _tceVersionSupported; @@ -1245,6 +1300,9 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The GLOBALTRANSACTIONS, DATACLASSIFICATION, TCE, and UTF8 support features are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.GlobalTransactions | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.UTF8Support; + // The AzureSQLDNSCaching feature is implicitly set + requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLDNSCaching; + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData); } @@ -2330,8 +2388,11 @@ internal void OnFeatureExtAck(int featureId, byte[] data) { if (RoutingInfo != null) { - return; + if (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING != featureId) { + return; + } } + switch (featureId) { case TdsEnums.FEATUREEXT_SRECOVERY: @@ -2518,6 +2579,40 @@ internal void OnFeatureExtAck(int featureId, byte[] data) _parser.DataClassificationVersion = (enabled == 0) ? TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED : supportedDataClassificationVersion; break; } + + case TdsEnums.FEATUREEXT_AZURESQLDNSCACHING: + { + SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AZURESQLDNSCACHING", ObjectID); + + if (data.Length < 1) + { + SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for AZURESQLDNSCACHING", ObjectID); + throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); + } + + if (1 == data[0]) { + IsSQLDNSCachingSupported = true; + _cleanSQLDNSCaching = false; + + if (RoutingInfo != null) + { + IsDNSCachingBeforeRedirectSupported = true; + } + } + else { + // we receive the IsSupported whose value is 0 + IsSQLDNSCachingSupported = false; + _cleanSQLDNSCaching = true; + } + + // need to add more steps for phrase 2 + // get IPv4 + IPv6 + Port number + // not put them in the DNS cache at this point but need to store them somewhere + // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag + + break; + } + default: { // Unknown feature ack diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index a9f3235faf..a5ae822290 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -214,6 +214,7 @@ public enum EnvChangeType : byte public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; + public const byte FEATUREEXT_AZURESQLDNSCACHING = 0x0B; [Flags] public enum FeatureExtension : uint @@ -226,6 +227,7 @@ public enum FeatureExtension : uint AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), + AzureSQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING - 1) } public const uint UTF8_IN_TDSCOLLATION = 0x4000000; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 81928b6da9..81f0a5e8a5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -157,6 +157,9 @@ internal sealed partial class TdsParser /// internal string EnclaveType { get; set; } + internal bool isTcpProtocol { get; set; } + internal string FQDNforDNSCahce { get; set; } + /// /// Get if data classification is enabled by the server. /// @@ -355,6 +358,9 @@ internal void Connect( _connHandler = connHandler; _loginWithFailover = withFailover; + // Clean up IsSQLDNSCachingSupported flag from previous status + _connHandler.IsSQLDNSCachingSupported = false; + uint sniStatus = TdsParserStateObjectFactory.Singleton.SNIStatus; if (sniStatus != TdsEnums.SNI_SUCCESS) @@ -402,8 +408,18 @@ internal void Connect( bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover; + FQDNforDNSCahce = serverInfo.ResolvedServerName; + + int commaPos = FQDNforDNSCahce.IndexOf(","); + if (commaPos != -1) + { + FQDNforDNSCahce = FQDNforDNSCahce.Substring(0, commaPos); + } + + _connHandler.pendingSQLDNSObject = null; + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, ref _sniSpnBuffer, false, true, fParallel, integratedSecurity); + out instanceName, ref _sniSpnBuffer, false, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -442,6 +458,13 @@ internal void Connect( uint result = _physicalStateObj.SniGetConnectionId(ref _connHandler._clientConnectionId); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); + + if (null == _connHandler.pendingSQLDNSObject) + { + // for DNS Caching phase 1 + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + } + SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); SendPreLoginHandshake(instanceName, encrypt); @@ -460,7 +483,7 @@ internal void Connect( // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, integratedSecurity); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -474,6 +497,12 @@ internal void Connect( Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); + if (null == _connHandler.pendingSQLDNSObject) + { + // for DNS Caching phase 1 + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + } + SendPreLoginHandshake(instanceName, encrypt); status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); @@ -3074,6 +3103,20 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) } } while (featureId != TdsEnums.FEATUREEXT_TERMINATOR); + // Write to DNS Cache or clean up DNS Cache for TCP protocol + bool ret = false; + if (_connHandler._cleanSQLDNSCaching) + { + ret = SQLDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + } + + if ( _connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null + && !SQLDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) + { + ret = SQLDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); + _connHandler.pendingSQLDNSObject = null; + } + // Check if column encryption was on and feature wasn't acknowledged and we aren't going to be routed to another server. if (Connection.RoutingInfo == null && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled @@ -7787,6 +7830,20 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD return len; } + internal int WriteAzureSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) + { + int len = 5; // 1byte = featureID, 4bytes = featureData length + + if (write) + { + // Write Feature ID + _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_AZURESQLDNSCACHING); + WriteInt(0, _physicalStateObj); // we don't send any data + } + + return len; + } + internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData? fedAuthFeatureExtensionData) { _physicalStateObj.SetTimeoutSeconds(rec.timeout); @@ -7944,6 +8001,11 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures length += WriteUTF8SupportFeatureRequest(false); } + if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + { + length += WriteAzureSQLDNSCachingFeatureRequest(false); + } + length++; // for terminator } @@ -8205,6 +8267,11 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures WriteUTF8SupportFeatureRequest(true); } + if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + { + WriteAzureSQLDNSCachingFeatureRequest(true); + } + _physicalStateObj.WriteByte(0xFF); // terminator } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 0aa77e5cae..921d72a385 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -143,7 +143,8 @@ internal SNIHandle( out byte[] instanceName, bool flushCache, bool fSync, - bool fParallel) + bool fParallel, + SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { try @@ -158,18 +159,18 @@ internal SNIHandle( } _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, - spnBuffer, instanceName, flushCache, fSync, timeout, fParallel); + spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, cachedDNSInfo); } } // constructs SNI Handle for MARS session - internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent) : base(IntPtr.Zero, true) + internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { try { } finally { - _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync); + _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, cachedDNSInfo); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 8e007a07e7..7782231195 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -762,7 +762,9 @@ private void ResetCancelAndProcessAttention() } } - internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool fParallel, bool isIntegratedSecurity = false); + internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool fParallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity = false); + + internal abstract void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo); internal abstract uint SniGetConnectionId(ref Guid clientConnectionId); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index 46ae4c4dfb..14a386d81f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -49,9 +49,9 @@ internal SNIMarsHandle CreateMarsSession(object callbackObject, bool async) protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) => SNIProxy.Singleton.PacketGetData(packet.ManagedPacket, _inBuff, ref dataSize); - internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity) + internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) { - _sessionHandle = SNIProxy.Singleton.CreateConnectionHandle(this, serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity); + _sessionHandle = SNIProxy.Singleton.CreateConnectionHandle(this, serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, cachedFQDN, ref pendingDNSInfo); if (_sessionHandle == null) { _parser.ProcessSNIError(this); @@ -63,6 +63,12 @@ internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSni } } + // The assignment will be happened right after we resolve DNS in managed SNI layer + internal override void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo) + { + // No-op + } + internal void ReadAsyncCallback(SNIPacket packet, uint error) { ReadAsyncCallback(IntPtr.Zero, PacketHandle.FromManagedPacket(packet), error); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 97825278ec..5ffe345122 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.Data.Common; +using System.Net; namespace Microsoft.Data.SqlClient { @@ -41,7 +42,62 @@ protected override void CreateSessionHandle(TdsParserStateObject physicalConnect Debug.Assert(physicalConnection is TdsParserStateObjectNative, "Expected a stateObject of type " + this.GetType()); TdsParserStateObjectNative nativeSNIObject = physicalConnection as TdsParserStateObjectNative; SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); - _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle); + + SQLDNSInfo cachedDNSInfo; + bool ret = SQLDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + + _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, cachedDNSInfo); + } + + internal override void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo) + { + uint result; + ushort portFromSNI = 0; + string IPStringFromSNI = string.Empty; + IPAddress IPFromSNI; + _parser.isTcpProtocol = false; + SNINativeMethodWrapper.ProviderEnum providerNumber = SNINativeMethodWrapper.ProviderEnum.INVALID_PROV; + + if (string.IsNullOrEmpty(userProtocol)) + { + + result = SNINativeMethodWrapper.SniGetProviderNumber(Handle, ref providerNumber); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetProviderNumber"); + _parser.isTcpProtocol = (providerNumber == SNINativeMethodWrapper.ProviderEnum.TCP_PROV); + } + else if (userProtocol == TdsEnums.TCP) + { + _parser.isTcpProtocol = true; + } + + // serverInfo.UserProtocol could be empty + if (_parser.isTcpProtocol) + { + result = SNINativeMethodWrapper.SniGetConnectionPort(Handle, ref portFromSNI); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionPort"); + + + result = SNINativeMethodWrapper.SniGetConnectionIPString(Handle, ref IPStringFromSNI); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionIPString"); + + pendingDNSInfo = new SQLDNSInfo(DNSCacheKey, null, null, portFromSNI.ToString()); + + if (IPAddress.TryParse(IPStringFromSNI, out IPFromSNI)) + { + if (System.Net.Sockets.AddressFamily.InterNetwork == IPFromSNI.AddressFamily) + { + pendingDNSInfo.AddrIPv4 = IPStringFromSNI; + } + else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == IPFromSNI.AddressFamily) + { + pendingDNSInfo.AddrIPv6 = IPStringFromSNI; + } + } + } + else + { + pendingDNSInfo = null; + } } private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) @@ -62,7 +118,7 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool fParallel, bool isIntegratedSecurity) + internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool fParallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) { // We assume that the loadSSPILibrary has been called already. now allocate proper length of buffer spnBuffer = null; @@ -93,7 +149,10 @@ internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSni } } - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel); + SQLDNSInfo cachedDNSInfo; + bool ret = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, cachedDNSInfo); } protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) 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 adc1a2b62c..3718ff1264 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -192,6 +192,9 @@ Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + + Microsoft\Data\SqlClient\SQLDNSCache.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index dd585ededa..9c141b8fcc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Text; using static Microsoft.Data.SqlClient.SNINativeMethodWrapper; namespace Microsoft.Data.SqlClient @@ -78,6 +79,17 @@ internal static class SNINativeManagedWrapperX64 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, ref IntPtr pbQInfo); + // kz start + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal static extern uint SNIGetPeerAddrStrWrapper([In] SNIHandle pConn, int bufferSize, StringBuilder addrBuffer, out uint addrLen); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); + // kz end + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIInitialize")] internal static extern uint SNIInitialize([In] IntPtr pmo); @@ -90,7 +102,8 @@ internal static extern uint SNIOpenWrapper( [MarshalAs(UnmanagedType.LPWStr)] string szConnect, [In] SNIHandle pConn, out IntPtr ppConn, - [MarshalAs(UnmanagedType.Bool)] bool fSync); + [MarshalAs(UnmanagedType.Bool)] bool fSync, + [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIPacketAllocateWrapper([In] SafeHandle pConn, IOType IOType); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index 339760c1a2..a6e1943aa5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; +using System.Text; using static Microsoft.Data.SqlClient.SNINativeMethodWrapper; namespace Microsoft.Data.SqlClient @@ -78,6 +79,17 @@ internal static class SNINativeManagedWrapperX86 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, ref IntPtr pbQInfo); + // kz start + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + internal static extern uint SNIGetPeerAddrStrWrapper([In] SNIHandle pConn, int bufferSize, StringBuilder addrBuffer, out uint addrLen); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); + // kz end + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIInitialize")] internal static extern uint SNIInitialize([In] IntPtr pmo); @@ -90,7 +102,8 @@ internal static extern uint SNIOpenWrapper( [MarshalAs(UnmanagedType.LPWStr)] string szConnect, [In] SNIHandle pConn, out IntPtr ppConn, - [MarshalAs(UnmanagedType.Bool)] bool fSync); + [MarshalAs(UnmanagedType.Bool)] bool fSync, + [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIPacketAllocateWrapper([In] SafeHandle pConn, IOType IOType); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs index 499b29b4d3..0a0868dc1b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs @@ -13,6 +13,7 @@ using System.Threading; using Microsoft.Data.Common; using Microsoft.Data.SqlClient; +using System.Text; namespace Microsoft.Data.SqlClient { @@ -50,6 +51,7 @@ internal static class SNINativeMethodWrapper internal const int LocalDBInvalidSqlUserInstanceDllPath = 55; internal const int LocalDBFailedToLoadDll = 56; internal const int LocalDBBadRuntime = 57; + internal const int SniIP6AddrStringBufferLength = 48; // from SNI layer internal static int SniMaxComposedSpnLength { @@ -352,6 +354,20 @@ internal unsafe struct SNI_CLIENT_CONSUMER_INFO public TransparentNetworkResolutionMode transparentNetworkResolution; public int totalTimeout; public bool isAzureSqlServerEndpoint; + public SNI_DNSCache_Info DNSCacheInfo; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct SNI_DNSCache_Info + { + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedFQDN; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpIPv4; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpIPv6; + [MarshalAs(UnmanagedType.LPWStr)] + public string wszCachedTcpPort; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] @@ -547,6 +563,29 @@ private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapp SNINativeManagedWrapperX86.SNIGetInfoWrapper(pConn, QType, ref pbQInfo); } + // kz start + private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum) + { + return s_is64bitProcess ? + SNINativeManagedWrapperX64.SNIGetInfoWrapper(pConn, QType, out portNum) : + SNINativeManagedWrapperX86.SNIGetInfoWrapper(pConn, QType, out portNum); + } + + private static uint SNIGetPeerAddrStrWrapper([In] SNIHandle pConn, int bufferSize, StringBuilder addrBuffer, out uint addrLen) + { + return s_is64bitProcess ? + SNINativeManagedWrapperX64.SNIGetPeerAddrStrWrapper(pConn, bufferSize, addrBuffer, out addrLen) : + SNINativeManagedWrapperX86.SNIGetPeerAddrStrWrapper(pConn, bufferSize, addrBuffer, out addrLen); + } + + private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum) + { + return s_is64bitProcess ? + SNINativeManagedWrapperX64.SNIGetInfoWrapper(pConn, QType, out provNum) : + SNINativeManagedWrapperX86.SNIGetInfoWrapper(pConn, QType, out provNum); + } + // kz end + private static uint SNIInitialize([In] IntPtr pmo) { return s_is64bitProcess ? @@ -566,11 +605,12 @@ private static uint SNIOpenWrapper( [MarshalAs(UnmanagedType.LPWStr)] string szConnect, [In] SNIHandle pConn, out IntPtr ppConn, - [MarshalAs(UnmanagedType.Bool)] bool fSync) + [MarshalAs(UnmanagedType.Bool)] bool fSync, + [In] ref SNI_DNSCache_Info pDNSCachedInfo) { return s_is64bitProcess ? - SNINativeManagedWrapperX64.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync) : - SNINativeManagedWrapperX86.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync); + SNINativeManagedWrapperX64.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ref pDNSCachedInfo) : + SNINativeManagedWrapperX86.SNIOpenWrapper(ref pConsumerInfo, szConnect, pConn, out ppConn, fSync, ref pDNSCachedInfo); } private static IntPtr SNIPacketAllocateWrapper([In] SafeHandle pConn, IOType IOType) @@ -687,22 +727,58 @@ internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } + + // kz start + internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) + { + return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); + } + + internal static uint SniGetConnectionPort(SNIHandle pConn, ref ushort portNum) + { + return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PEERPORT, out portNum); + } + + internal static uint SniGetConnectionIPString(SNIHandle pConn, ref string connIPStr) + { + UInt32 ret; + uint ERROR_SUCCESS = 0; + uint connIPLen = 0; + + int bufferSize = SniIP6AddrStringBufferLength; + StringBuilder addrBuffer = new StringBuilder(bufferSize); + + ret = SNIGetPeerAddrStrWrapper(pConn, bufferSize, addrBuffer, out connIPLen); + Debug.Assert(ret == ERROR_SUCCESS, "SNIGetPeerAddrStrWrapper fail"); + + connIPStr = addrBuffer.ToString(0, Convert.ToInt32(connIPLen)); + + return ret; + } + + // kz end internal static uint SNIInitialize() { return SNIInitialize(IntPtr.Zero); } - internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync) + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS Sni_Consumer_Info native_consumerInfo = new Sni_Consumer_Info(); MarshalConsumerInfo(consumerInfo, ref native_consumerInfo); - return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync); + SNI_DNSCache_Info native_cachedDNSInfo = new SNI_DNSCache_Info(); + native_cachedDNSInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; + native_cachedDNSInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; + native_cachedDNSInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; + native_cachedDNSInfo.wszCachedTcpPort = cachedDNSInfo?.Port; + + return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, Int32 transparentNetworkResolutionStateNo, Int32 totalTimeout, Boolean isAzureSqlServerEndpoint) + internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, Int32 transparentNetworkResolutionStateNo, Int32 totalTimeout, Boolean isAzureSqlServerEndpoint, SQLDNSInfo cachedDNSInfo) { fixed (byte* pin_instanceName = &instanceName[0]) { @@ -737,6 +813,11 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons }; clientConsumerInfo.totalTimeout = totalTimeout; + clientConsumerInfo.DNSCacheInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; + clientConsumerInfo.DNSCacheInfo.wszCachedTcpPort = cachedDNSInfo?.Port; + if (spnBuffer != null) { fixed (byte* pin_spnBuffer = &spnBuffer[0]) 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 9ecacf253c..dfcaf27f0b 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 @@ -711,6 +711,54 @@ override public string Database } } + /// + /// for internal test only + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal string SQLDNSCachingSupportedState + { + get + { + SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds); + string result; + + if (null != innerConnection) + { + result = innerConnection.IsSQLDNSCachingSupported ? "true": "false"; + } + else + { + result = "innerConnection is null!"; + } + + return result; + } + } + + /// + /// for internal test only + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal string SQLDNSCachingSupportedStateBeforeRedirect + { + get + { + SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds); + string result; + + if (null != innerConnection) + { + result = innerConnection.IsDNSCachingBeforeRedirectSupported ? "true": "false"; + } + else + { + result = "innerConnection is null!"; + } + + return result; + } + } + /// [ Browsable(true), 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 5a778a1b43..c1a88447d8 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 @@ -142,6 +142,61 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa ClientCertificateRetrievalCallback _clientCallback; SqlClientOriginalNetworkAddressInfo _originalNetworkAddressInfo; + internal bool _cleanSQLDNSCaching = false; + + private bool _serverSupportsDNSCaching = false; + + /// + /// Get or set if AzureSQLDNSCaching FeatureExtAck is supported by the server. + /// + internal bool IsSQLDNSCachingSupported + { + get + { + return _serverSupportsDNSCaching; + } + set + { + _serverSupportsDNSCaching = value; + } + } + + private bool _SQLDNSRetryEnabled = false; + + /// + /// Get or set if we need retrying with IP received from FeatureExtAck. + /// + internal bool IsSQLDNSRetryEnabled + { + get + { + return _SQLDNSRetryEnabled; + } + set + { + _SQLDNSRetryEnabled = value; + } + } + + private bool DNSCachingBeforeRedirect = false; + + /// + /// Get or set if the control ring send redirect token and AzureSQLDNSCaching FeatureExtAck with true + /// + internal bool IsDNSCachingBeforeRedirectSupported + { + get + { + return DNSCachingBeforeRedirect; + } + set + { + DNSCachingBeforeRedirect = value; + } + } + + internal SQLDNSInfo pendingSQLDNSObject = null; + // TCE flags internal byte _tceVersionSupported; @@ -1528,6 +1583,9 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLSupport; } + // The AzureSQLDNSCaching feature is implicitly set + requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLDNSCaching; + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo); } @@ -2811,8 +2869,11 @@ internal void OnFeatureExtAck(int featureId, byte[] data) { if (_routingInfo != null) { - return; + if (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING != featureId) { + return; + } } + switch (featureId) { case TdsEnums.FEATUREEXT_SRECOVERY: @@ -3026,6 +3087,40 @@ internal void OnFeatureExtAck(int featureId, byte[] data) break; } + case TdsEnums.FEATUREEXT_AZURESQLDNSCACHING: + { + SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AZURESQLDNSCACHING", ObjectID); + + if (data.Length < 1) + { + SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for AZURESQLDNSCACHING", ObjectID); + throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); + } + + if (1 == data[0]) { + IsSQLDNSCachingSupported = true; + _cleanSQLDNSCaching = false; + + if (_routingInfo != null) + { + IsDNSCachingBeforeRedirectSupported = true; + } + } + else { + // we receive the IsSupported whose value is 0 + IsSQLDNSCachingSupported = false; + _cleanSQLDNSCaching = true; + } + + // need to add more steps for phrase 2 + // get IPv4 + IPv6 + Port number + // not put them in the DNS cache at this point but need to store them somewhere + + // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag + + break; + } + default: { // Unknown feature ack diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index b23d97f2c1..4272af1d31 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -206,6 +206,7 @@ internal static class TdsEnums public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; + public const byte FEATUREEXT_AZURESQLDNSCACHING = 0x0B; [Flags] public enum FeatureExtension : uint @@ -217,7 +218,8 @@ public enum FeatureExtension : uint GlobalTransactions = 1 << (TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS - 1), AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), - UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), + UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), + AzureSQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING - 1) } public const uint UTF8_IN_TDSCOLLATION = 0x4000000; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index c61bbb4002..36994d6924 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -16,6 +16,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; +using System.Net; using Microsoft.Data.Common; using Microsoft.Data.Sql; using Microsoft.Data.SqlClient.DataClassification; @@ -279,6 +280,10 @@ internal bool IsColumnEncryptionSupported /// internal string EnclaveType { get; set; } + internal bool isTcpProtocol { get; set; } + + internal string FQDNforDNSCahce { get; set; } + /// /// Get if data classification is enabled by the server. /// @@ -494,6 +499,9 @@ internal void Connect(ServerInfo serverInfo, _connHandler = connHandler; _loginWithFailover = withFailover; + // Clean up IsSQLDNSCachingSupported flag from previous status + _connHandler.IsSQLDNSCachingSupported = false; + UInt32 sniStatus = SNILoadHandle.SingletonInstance.SNIStatus; if (sniStatus != TdsEnums.SNI_SUCCESS) { @@ -557,8 +565,16 @@ internal void Connect(ServerInfo serverInfo, int totalTimeout = _connHandler.ConnectionOptions.ConnectTimeout; + FQDNforDNSCahce = serverInfo.ResolvedServerName; + + int commaPos = FQDNforDNSCahce.IndexOf(","); + if (commaPos != -1) + { + FQDNforDNSCahce = FQDNforDNSCahce.Substring(0, commaPos); + } + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout); + out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout, FQDNforDNSCahce); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -597,6 +613,9 @@ internal void Connect(ServerInfo serverInfo, UInt32 result = SNINativeMethodWrapper.SniGetConnectionId(_physicalStateObj.Handle, ref _connHandler._clientConnectionId); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); + + // for DNS Caching phase 1 + AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); // UNDONE - send "" for instance now, need to fix later SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); @@ -618,7 +637,7 @@ internal void Connect(ServerInfo serverInfo, // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, serverInfo.ResolvedServerName); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -632,6 +651,9 @@ internal void Connect(ServerInfo serverInfo, Debug.Assert(retCode == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); SqlClientEventSource.Log.TraceEvent(" Sending prelogin handshake", "SEC"); + // for DNS Caching phase 1 + AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); + SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, out marsCapable, out _connHandler._fedAuthRequired); @@ -659,6 +681,57 @@ internal void Connect(ServerInfo serverInfo, return; } + internal void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey) + { + UInt32 result; + ushort portFromSNI = 0; + string IPStringFromSNI = string.Empty; + IPAddress IPFromSNI; + isTcpProtocol = false; + SNINativeMethodWrapper.ProviderEnum providerNumber = SNINativeMethodWrapper.ProviderEnum.INVALID_PROV; + + if (string.IsNullOrEmpty(userProtocol)) + { + + result = SNINativeMethodWrapper.SniGetProviderNumber(_physicalStateObj.Handle, ref providerNumber); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetProviderNumber"); + isTcpProtocol = (providerNumber == SNINativeMethodWrapper.ProviderEnum.TCP_PROV); + } + else if (userProtocol == TdsEnums.TCP) + { + isTcpProtocol = true; + } + + // serverInfo.UserProtocol could be empty + if (isTcpProtocol) + { + result = SNINativeMethodWrapper.SniGetConnectionPort(_physicalStateObj.Handle, ref portFromSNI); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionPort"); + + + result = SNINativeMethodWrapper.SniGetConnectionIPString(_physicalStateObj.Handle, ref IPStringFromSNI); + Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionIPString"); + + _connHandler.pendingSQLDNSObject = new SQLDNSInfo(DNSCacheKey, null, null, portFromSNI.ToString()); + + if (IPAddress.TryParse(IPStringFromSNI, out IPFromSNI)) + { + if (System.Net.Sockets.AddressFamily.InterNetwork == IPFromSNI.AddressFamily) + { + _connHandler.pendingSQLDNSObject.AddrIPv4 = IPStringFromSNI; + } + else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == IPFromSNI.AddressFamily) + { + _connHandler.pendingSQLDNSObject.AddrIPv6 = IPStringFromSNI; + } + } + } + else + { + _connHandler.pendingSQLDNSObject = null; + } + } + internal void RemoveEncryption() { Debug.Assert((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN, "Invalid encryption option state"); @@ -3458,6 +3531,20 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) } } while (featureId != TdsEnums.FEATUREEXT_TERMINATOR); + // Write to DNS Cache or clean up DNS Cache for TCP protocol + bool ret = false; + if (_connHandler._cleanSQLDNSCaching) + { + ret = SQLDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + } + + if ( _connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null + && !SQLDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) + { + ret = SQLDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); + _connHandler.pendingSQLDNSObject = null; + } + // Check if column encryption was on and feature wasn't acknowledged and we aren't going to be routed to another server. if (this.Connection.RoutingInfo == null && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled @@ -8530,6 +8617,20 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD return len; } + internal int WriteAzureSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) + { + int len = 5; // 1byte = featureID, 4bytes = featureData length + + if (write) + { + // Write Feature ID + _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_AZURESQLDNSCACHING); + WriteInt(0, _physicalStateObj); // we don't send any data + } + + return len; + } + internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, @@ -8717,6 +8818,12 @@ internal void TdsLogin(SqlLogin rec, { length += WriteUTF8SupportFeatureRequest(false); } + + if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + { + length += WriteAzureSQLDNSCachingFeatureRequest(false); + } + length++; // for terminator } } @@ -8988,6 +9095,12 @@ internal void TdsLogin(SqlLogin rec, { WriteUTF8SupportFeatureRequest(true); } + + if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + { + WriteAzureSQLDNSCachingFeatureRequest(true); + } + _physicalStateObj.WriteByte(0xFF); // terminator } } // try diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index e351d36d61..30e874995c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -149,7 +149,8 @@ internal SNIHandle( bool fSync, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, - int totalTimeout) + int totalTimeout, + SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { @@ -171,19 +172,19 @@ internal SNIHandle( int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState; _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, transparentNetworkResolutionStateNo, totalTimeout, - ADP.IsAzureSqlServerEndpoint(serverName)); + ADP.IsAzureSqlServerEndpoint(serverName), cachedDNSInfo); } } // constructs SNI Handle for MARS session - internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent) : base(IntPtr.Zero, true) + internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent, SQLDNSInfo cachedDNSInfo) : base(IntPtr.Zero, true) { RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { - _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync); + _status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync, cachedDNSInfo); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 63f0f7d68b..c7c395650a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -294,7 +294,11 @@ internal TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, bo SetPacketSize(_parser._physicalStateObj._outBuff.Length); SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); - _sessionHandle = new SNIHandle(myInfo, physicalConnection); + + SQLDNSInfo cachedDNSInfo; + bool ret = SQLDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + + _sessionHandle = new SNIHandle(myInfo, physicalConnection, cachedDNSInfo); if (_sessionHandle.Status != TdsEnums.SNI_SUCCESS) { AddError(parser.ProcessSNIError(this)); @@ -820,7 +824,7 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, byte[] spnBuffer, bool flushCache, bool async, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout) + internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, byte[] spnBuffer, bool flushCache, bool async, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, string cachedFQDN) { SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); @@ -842,7 +846,13 @@ internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeo timeout = 0; } } - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout); + + // serverName : serverInfo.ExtendedServerName + // may not use this serverName as key + SQLDNSInfo cachedDNSInfo; + bool ret = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, cachedDNSInfo); } internal bool Deactivate() diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs new file mode 100644 index 0000000000..894dcdf505 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Collections.Concurrent; + +namespace Microsoft.Data.SqlClient +{ + internal class SQLDNSCache + { + private static readonly SQLDNSCache _SQLDNSCache = new SQLDNSCache(); + private static readonly int initialCapacity = 100; + private ConcurrentDictionary DNSInfoCache; + + // singleton instance + public static SQLDNSCache Instance { get { return _SQLDNSCache; } } + + private SQLDNSCache() + { + int level = 4 * Environment.ProcessorCount; + DNSInfoCache = new ConcurrentDictionary(concurrencyLevel: level, + capacity: initialCapacity, + comparer: StringComparer.OrdinalIgnoreCase); + } + + internal bool AddDNSInfo(SQLDNSInfo item) + { + if (null != item) + { + if (DNSInfoCache.ContainsKey(item.FQDN)) + { + + DeleteDNSInfo(item.FQDN); + } + + return DNSInfoCache.TryAdd(item.FQDN, item); + } + + return false; + } + + internal bool DeleteDNSInfo(string FQDN) + { + SQLDNSInfo value; + return DNSInfoCache.TryRemove(FQDN, out value); + } + + internal bool GetDNSInfo(string FQDN, out SQLDNSInfo result) + { + return DNSInfoCache.TryGetValue(FQDN, out result); + } + + internal bool IsDuplicate(SQLDNSInfo newItem) + { + if (null != newItem) + { + SQLDNSInfo oldItem; + if (GetDNSInfo(newItem.FQDN, out oldItem)) + { + return (newItem.AddrIPv4 == oldItem.AddrIPv4 && + newItem.AddrIPv6 == oldItem.AddrIPv6 && + newItem.Port == oldItem.Port); + } + } + + return false; + } + + } + + internal class SQLDNSInfo + { + public string FQDN { get; set; } + public string AddrIPv4 { get; set; } + public string AddrIPv6 { get; set; } + public string Port { get; set; } + + internal SQLDNSInfo(string FQDN, string ipv4, string ipv6, string port) + { + this.FQDN = FQDN; + AddrIPv4 = ipv4; + AddrIPv6 = ipv6; + Port = port; + } + } +} From 4c3e23a94000882e1d9cab5e24d1e7c806b10865 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 5 Jun 2020 18:48:18 -0700 Subject: [PATCH 2/9] Add DNS Caching test --- .../ManualTests/DataCommon/DataTestUtility.cs | 20 +++++ ....Data.SqlClient.ManualTesting.Tests.csproj | 1 + .../SQL/DNSCachingTest/DNSCachingTest.cs | 79 +++++++++++++++++++ .../tests/ManualTests/config.default.json | 7 +- 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 590ae5a5a6..c8d8172810 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -41,6 +41,12 @@ public static class DataTestUtility public static readonly bool SupportsFileStream = false; public static readonly bool UseManagedSNIOnWindows = false; + public static readonly string DNSCachingConnString = null; + public static readonly string DNSCachingServerCR = null; // this is for the control ring + public static readonly string DNSCachingServerTR = null; // this is for the tenant ring + public static readonly bool IsDNSCachingSupportedCR = false; // this is for the control ring + public static readonly bool IsDNSCachingSupportedTR = false; // this is for the tenant ring + public const string UdtTestDbName = "UdtTestDb"; public const string AKVKeyName = "TestSqlClientAzureKeyVaultProvider"; private const string ManagedNetworkingAppContextSwitch = "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"; @@ -71,6 +77,11 @@ private class Config public bool SupportsLocalDb = false; public bool SupportsFileStream = false; public bool UseManagedSNIOnWindows = false; + public string DNSCachingConnString = null; + public string DNSCachingServerCR = null; // this is for the control ring + public string DNSCachingServerTR = null; // this is for the tenant ring + public bool IsDNSCachingSupportedCR = false; // this is for the control ring + public bool IsDNSCachingSupportedTR = false; // this is for the tenant ring } static DataTestUtility() @@ -94,6 +105,12 @@ static DataTestUtility() TracingEnabled = c.TracingEnabled; UseManagedSNIOnWindows = c.UseManagedSNIOnWindows; + DNSCachingConnString = c.DNSCachingConnString; + DNSCachingServerCR = c.DNSCachingServerCR; + DNSCachingServerTR = c.DNSCachingServerTR; + IsDNSCachingSupportedCR = c.IsDNSCachingSupportedCR; + IsDNSCachingSupportedTR = c.IsDNSCachingSupportedTR; + if (TracingEnabled) { TraceListener = new DataTestUtility.TraceEventListener(); @@ -258,6 +275,9 @@ public static bool IsSupportedDataClassification() } return true; } + + public static bool IsDNSCachingSetup() => !string.IsNullOrEmpty(DNSCachingConnString); + public static bool IsUdtTestDatabasePresent() => IsDatabasePresent(UdtTestDbName); public static bool AreConnStringsSetup() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 52c66b3d1b..9e03a7792a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -189,6 +189,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs new file mode 100644 index 0000000000..97fad07afa --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + + public class DNSCachingTest + { + public static Assembly systemData = Assembly.GetAssembly(typeof(SqlConnection)); + public static Type SQLDNSCacheType = systemData.GetType("Microsoft.Data.SqlClient.SQLDNSCache"); + public static Type SQLDNSInfoType = systemData.GetType("Microsoft.Data.SqlClient.SQLDNSInfo"); + public static MethodInfo SQLDNSCacheGetDNSInfo = SQLDNSCacheType.GetMethod("GetDNSInfo", BindingFlags.Instance | BindingFlags.NonPublic); + + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDNSCachingSetup))] + public void DNSCachingIsSupportedFlag() + { + string expectedDNSCachingSupportedCR = DataTestUtility.IsDNSCachingSupportedCR ? "true" : "false"; + string expectedDNSCachingSupportedTR = DataTestUtility.IsDNSCachingSupportedTR ? "true" : "false"; + + using(SqlConnection connection = new SqlConnection(DataTestUtility.DNSCachingConnString)) + { + connection.Open(); + + string isSupportedStateTR = (string)typeof(SqlConnection).GetProperty("SQLDNSCachingSupportedState", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(connection); + string isSupportedStateCR = (string)typeof(SqlConnection).GetProperty("SQLDNSCachingSupportedStateBeforeRedirect", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(connection); + Assert.Equal(expectedDNSCachingSupportedCR, isSupportedStateCR); + Assert.Equal(expectedDNSCachingSupportedTR, isSupportedStateTR); + } + } + + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDNSCachingSetup))] + public void DNSCachingGetDNSInfo() + { + using(SqlConnection connection = new SqlConnection(DataTestUtility.DNSCachingConnString)) + { + connection.Open(); + } + + var SQLDNSCacheInstance = SQLDNSCacheType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public).GetValue(null); + + var serverList = new List>(); + serverList.Add(new KeyValuePair(DataTestUtility.DNSCachingServerCR, DataTestUtility.IsDNSCachingSupportedCR)); + serverList.Add(new KeyValuePair(DataTestUtility.DNSCachingServerTR, DataTestUtility.IsDNSCachingSupportedTR)); + + foreach(var server in serverList) + { + object[] parameters; + bool ret; + + if (!string.IsNullOrEmpty(server.Key)) + { + parameters = new object[] { server.Key, null }; + ret = (bool)SQLDNSCacheGetDNSInfo.Invoke(SQLDNSCacheInstance, parameters); + + if (server.Value) + { + Assert.NotNull(parameters[1]); + Assert.Equal(server.Key, (string)SQLDNSInfoType.GetProperty("FQDN").GetValue(parameters[1])); + } + else + { + Assert.Null(parameters[1]); + } + } + } + } + + } + +} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json index 54fdd927f6..87e78968ff 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json @@ -14,5 +14,10 @@ "SupportsIntegratedSecurity": true, "SupportsLocalDb": false, "SupportsFileStream": false, - "UseManagedSNIOnWindows": false + "UseManagedSNIOnWindows": false, + "DNSCachingConnString": "", + "DNSCachingServerCR": "", + "DNSCachingServerTR": "", + "IsDNSCachingSupportedCR": false, + "IsDNSCachingSupportedTR": false } From cd2cd94e8d5f6c506764f613d73fa178be7f87e3 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 8 Jun 2020 14:51:55 -0700 Subject: [PATCH 3/9] Add public method to return DNSCaching related connection properties --- .../SqlConnection.xml | 14 ++++++ .../netcore/ref/Microsoft.Data.SqlClient.cs | 3 ++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 11 +++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 3 ++ .../Microsoft/Data/SqlClient/SqlConnection.cs | 11 +++++ .../FunctionalTests/SqlConnectionTest.cs | 49 +++++++++++++++++++ .../SQL/DNSCachingTest/DNSCachingTest.cs | 8 +-- 7 files changed, 96 insertions(+), 3 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index e2980f9d37..1eafca2f31 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1055,6 +1055,20 @@ GO ]]> + + Returns a name value pair collection of internal properties at the point in time the method is called. + Returns a reference of type of (string, object) items. + + + + Gets a string that contains the version of the instance of SQL Server to which the client is connected. The version of the instance of SQL Server. diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 38857196bf..2b9c8cd8d4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -611,6 +611,9 @@ public void Open(SqlConnectionOverrides overrides) { } public void ResetStatistics() { } /// public System.Collections.IDictionary RetrieveStatistics() { throw null; } + + /// + public System.Collections.Generic.IDictionary RetrieveInternalInfo() { throw null; } } /// public enum SqlConnectionOverrides 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 a80ecd6b70..c3f8004654 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 @@ -1968,6 +1968,17 @@ private void UpdateStatistics() Statistics.UpdateStatistics(); } + /// + public IDictionary RetrieveInternalInfo() + { + IDictionary internalDictionary = new Dictionary(); + + internalDictionary.Add("SQLDNSCachingSupportedState", SQLDNSCachingSupportedState); + internalDictionary.Add("SQLDNSCachingSupportedStateBeforeRedirect", SQLDNSCachingSupportedStateBeforeRedirect); + + return internalDictionary; + } + /// object ICloneable.Clone() => new SqlConnection(this); diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index f149a75266..a8a615a8e7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -787,6 +787,9 @@ public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections. public void ResetStatistics() { } /// public System.Collections.IDictionary RetrieveStatistics() { throw null; } + + /// + public System.Collections.Generic.IDictionary RetrieveInternalInfo() { throw null; } } /// public enum SqlConnectionColumnEncryptionSetting 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 dfcaf27f0b..a3db44c2da 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 @@ -2703,6 +2703,17 @@ private void UpdateStatistics() Statistics.UpdateStatistics(); } + /// + public IDictionary RetrieveInternalInfo() + { + IDictionary internalDictionary = new Dictionary(); + + internalDictionary.Add("SQLDNSCachingSupportedState", SQLDNSCachingSupportedState); + internalDictionary.Add("SQLDNSCachingSupportedStateBeforeRedirect", SQLDNSCachingSupportedStateBeforeRedirect); + + return internalDictionary; + } + // // UDT SUPPORT // diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs index 244e008f96..f2870aae1e 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs @@ -4,12 +4,19 @@ using System; using System.Data; +using System.Collections.Generic; using Xunit; namespace Microsoft.Data.SqlClient.Tests { public partial class SqlConnectionTest { + private static readonly string[] s_retrieveInternalInfoKeys = + { + "SQLDNSCachingSupportedState", + "SQLDNSCachingSupportedStateBeforeRedirect" + }; + [Fact] public void Constructor1() { @@ -1212,5 +1219,47 @@ public void ServerVersion_Connection_Closed() Assert.NotNull(ex.Message); } } + + [Fact] + public void RetrieveInternalInfo_Success() + { + SqlConnection cn = new SqlConnection(); + IDictionary d = cn.RetrieveInternalInfo(); + + Assert.NotNull(d); + } + + [Fact] + public void RetrieveInternalInfo_ExpectedKeysInDictionary_Success() + { + SqlConnection cn = new SqlConnection(); + IDictionary d = cn.RetrieveInternalInfo(); + + Assert.NotEmpty(d); + Assert.Equal(s_retrieveInternalInfoKeys.Length, d.Count); + + Assert.NotEmpty(d.Keys); + Assert.Equal(s_retrieveInternalInfoKeys.Length, d.Keys.Count); + + Assert.NotEmpty(d.Values); + Assert.Equal(s_retrieveInternalInfoKeys.Length, d.Values.Count); + + foreach(string key in s_retrieveInternalInfoKeys) + { + Assert.True(d.ContainsKey(key)); + + d.TryGetValue(key, out object value); + Assert.NotNull(value); + Assert.IsType(value); + } + } + + [Fact] + public void RetrieveInternalInfo_UnexpectedKeysInDictionary_Success() + { + SqlConnection cn = new SqlConnection(); + IDictionary d = cn.RetrieveInternalInfo(); + Assert.False(d.ContainsKey("Foo")); + } } } 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 97fad07afa..f47c819f82 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -29,13 +29,15 @@ public void DNSCachingIsSupportedFlag() { connection.Open(); - string isSupportedStateTR = (string)typeof(SqlConnection).GetProperty("SQLDNSCachingSupportedState", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(connection); - string isSupportedStateCR = (string)typeof(SqlConnection).GetProperty("SQLDNSCachingSupportedStateBeforeRedirect", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(connection); + IDictionary dictionary = connection.RetrieveInternalInfo(); + bool ret = dictionary.TryGetValue("SQLDNSCachingSupportedState", out object val); + ret = dictionary.TryGetValue("SQLDNSCachingSupportedStateBeforeRedirect", out object valBeforeRedirect); + string isSupportedStateTR = (string)val; + string isSupportedStateCR = (string)valBeforeRedirect; Assert.Equal(expectedDNSCachingSupportedCR, isSupportedStateCR); Assert.Equal(expectedDNSCachingSupportedTR, isSupportedStateTR); } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDNSCachingSetup))] public void DNSCachingGetDNSInfo() From b3623d5a828240f33701206d65a391b6e663cd6d Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Tue, 9 Jun 2020 18:42:45 -0700 Subject: [PATCH 4/9] Address comments Removed temporary comments, added some comments for logic explanation, updated formatting and changed initial capacity for DNS Cache. --- .../src/Interop/SNINativeMethodWrapper.Windows.cs | 4 ---- .../src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs | 9 +++++++++ .../src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ++-- .../Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs | 2 -- .../Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs | 2 -- .../src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs | 7 +------ .../netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs | 4 ++-- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 3 +++ .../src/Microsoft/Data/SqlClient/SQLDNSCache.cs | 3 +-- .../ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs | 4 +--- .../tests/ManualTests/config.default.json | 7 +------ 11 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index 795c14c5b1..d27d210ab8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -253,7 +253,6 @@ internal struct SNI_Error [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out Guid pbQInfo); - // kz start [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); @@ -262,7 +261,6 @@ internal struct SNI_Error [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); - // kz end [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIInitialize([In] IntPtr pmo); @@ -313,7 +311,6 @@ internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } - // kz start internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); @@ -338,7 +335,6 @@ internal static uint SniGetConnectionIPString(SNIHandle pConn, ref string connIP return ret; } - // kz end internal static uint SNIInitialize() { 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 32280880eb..4b03f7c85b 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 @@ -127,6 +127,9 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba bool reportError = true; + // We will always first try to connect with serverName as before and let the DNS server to resolve the serverName. + // If the DSN resolution fails, we will try with IPs in the DNS cache if existed. We try with IPv4 first and followed by IPv6 if + // IPv4 fails. The exceptions will be throw to upper level and be handled as before. try { if (parallel) @@ -225,6 +228,9 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba _status = TdsEnums.SNI_SUCCESS; } + // Connect to server with hostName and port in parellel mode. + // The IP information will be collected temporarily as the pendingDNSInfo but is not stored in the DNS cache at this point. + // Only write to the DNS cache when we receive IsSupported flag as true in the Feature Ext Ack from server. private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool isInfiniteTimeOut, ref bool callerReportError, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { Socket availableSocket = null; @@ -276,6 +282,9 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i } + // Connect to server with hostName and port. + // The IP information will be collected temporarily as the pendingDNSInfo but is not stored in the DNS cache at this point. + // Only write to the DNS cache when we receive IsSupported flag as true in the Feature Ext Ack from server. private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); 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 c3f8004654..8a347fae48 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 @@ -515,7 +515,7 @@ public override string Database } /// - /// for internal use only + /// To indicate the IsSupported flag sent by the server for DNS Caching. This property is for internal testing only. /// internal string SQLDNSCachingSupportedState { @@ -538,7 +538,7 @@ internal string SQLDNSCachingSupportedState } /// - /// for internal use only + /// To indicate the IsSupported flag sent by the server for DNS Caching before redirection. This property is for internal testing only. /// internal string SQLDNSCachingSupportedStateBeforeRedirect { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index 9c141b8fcc..93f3d46976 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -79,7 +79,6 @@ internal static class SNINativeManagedWrapperX64 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, ref IntPtr pbQInfo); - // kz start [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); @@ -88,7 +87,6 @@ internal static class SNINativeManagedWrapperX64 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); - // kz end [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIInitialize")] internal static extern uint SNIInitialize([In] IntPtr pmo); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index a6e1943aa5..37226a1dc2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -79,7 +79,6 @@ internal static class SNINativeManagedWrapperX86 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, ref IntPtr pbQInfo); - // kz start [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum); @@ -88,7 +87,6 @@ internal static class SNINativeManagedWrapperX86 [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ProviderEnum provNum); - // kz end [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIInitialize")] internal static extern uint SNIInitialize([In] IntPtr pmo); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs index 0a0868dc1b..88051bcb8e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs @@ -563,7 +563,6 @@ private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapp SNINativeManagedWrapperX86.SNIGetInfoWrapper(pConn, QType, ref pbQInfo); } - // kz start private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapper.QTypes QType, out ushort portNum) { return s_is64bitProcess ? @@ -584,7 +583,6 @@ private static uint SNIGetInfoWrapper([In] SNIHandle pConn, SNINativeMethodWrapp SNINativeManagedWrapperX64.SNIGetInfoWrapper(pConn, QType, out provNum) : SNINativeManagedWrapperX86.SNIGetInfoWrapper(pConn, QType, out provNum); } - // kz end private static uint SNIInitialize([In] IntPtr pmo) { @@ -727,8 +725,7 @@ internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } - - // kz start + internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); @@ -756,8 +753,6 @@ internal static uint SniGetConnectionIPString(SNIHandle pConn, ref string connIP return ret; } - // kz end - internal static uint SNIInitialize() { return SNIInitialize(IntPtr.Zero); 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 a3db44c2da..dfc32acb66 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 @@ -712,7 +712,7 @@ override public string Database } /// - /// for internal test only + /// To indicate the IsSupported flag sent by the server for DNS Caching. This property is for internal testing only. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal string SQLDNSCachingSupportedState @@ -736,7 +736,7 @@ internal string SQLDNSCachingSupportedState } /// - /// for internal test only + /// To indicate the IsSupported flag sent by the server for DNS Caching before redirection. This property is for internal testing only. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] internal string SQLDNSCachingSupportedStateBeforeRedirect diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 36994d6924..ff0c7bba7a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -681,6 +681,9 @@ internal void Connect(ServerInfo serverInfo, return; } + // Retrieve the IP and port number from native SNI for TCP protocol. The IP information is stored temporarily in the + // pendingSQLDNSObject but not in the DNS Cache at this point. We only add items to the DNS Cache after we receive the + // IsSupported flag as true in the feature ext ack from server. internal void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey) { UInt32 result; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs index 894dcdf505..49b20562ad 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs @@ -10,7 +10,7 @@ namespace Microsoft.Data.SqlClient internal class SQLDNSCache { private static readonly SQLDNSCache _SQLDNSCache = new SQLDNSCache(); - private static readonly int initialCapacity = 100; + private static readonly int initialCapacity = 101; // give some prime number here according to MSDN docs. It will be resized if reached capacity. private ConcurrentDictionary DNSInfoCache; // singleton instance @@ -66,7 +66,6 @@ internal bool IsDuplicate(SQLDNSInfo newItem) return false; } - } internal class SQLDNSInfo 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 f47c819f82..470356c700 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -75,7 +75,5 @@ public void DNSCachingGetDNSInfo() } } } - } - -} \ No newline at end of file +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json index 87e78968ff..54fdd927f6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json @@ -14,10 +14,5 @@ "SupportsIntegratedSecurity": true, "SupportsLocalDb": false, "SupportsFileStream": false, - "UseManagedSNIOnWindows": false, - "DNSCachingConnString": "", - "DNSCachingServerCR": "", - "DNSCachingServerTR": "", - "IsDNSCachingSupportedCR": false, - "IsDNSCachingSupportedTR": false + "UseManagedSNIOnWindows": false } From 02002c8b91c14c89cd500af3d977424f7f1d2a7d Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Wed, 10 Jun 2020 11:50:29 -0700 Subject: [PATCH 5/9] Address comments part2 Remove Azure-prefix from variable names and rename DNS cache --- .../src/Microsoft.Data.SqlClient.csproj | 4 ++-- .../Data/SqlClient/SNI/SNITcpHandle.cs | 2 +- .../Data/SqlClient/SqlInternalConnectionTds.cs | 13 ++++++------- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 4 ++-- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 18 +++++++++--------- .../SqlClient/TdsParserStateObjectNative.cs | 4 ++-- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 ++-- .../Data/SqlClient/SqlInternalConnectionTds.cs | 17 ++++++++--------- .../src/Microsoft/Data/SqlClient/TdsEnums.cs | 4 ++-- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 18 +++++++++--------- .../Data/SqlClient/TdsParserStateObject.cs | 4 ++-- .../{SQLDNSCache.cs => SQLFallbackDNSCache.cs} | 8 ++++---- .../SQL/DNSCachingTest/DNSCachingTest.cs | 8 ++++---- 13 files changed, 53 insertions(+), 55 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/{SQLDNSCache.cs => SQLFallbackDNSCache.cs} (90%) 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 1b9b7a79df..7521877cad 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -138,8 +138,8 @@ Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs - - Microsoft\Data\SqlClient\SQLDNSCache.cs + + Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs 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 4b03f7c85b..f74b02f3a7 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 @@ -110,7 +110,7 @@ public SNITCPHandle(string serverName, int port, long timerExpire, object callba _sendSync = new object(); SQLDNSInfo cachedDNSInfo; - bool hasCachedDNSInfo = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + bool hasCachedDNSInfo = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); try { 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 932c991887..52f194815b 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 @@ -1300,8 +1300,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The GLOBALTRANSACTIONS, DATACLASSIFICATION, TCE, and UTF8 support features are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.GlobalTransactions | TdsEnums.FeatureExtension.DataClassification | TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.UTF8Support; - // The AzureSQLDNSCaching feature is implicitly set - requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLDNSCaching; + // The SQLDNSCaching feature is implicitly set + requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData); } @@ -2388,7 +2388,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) { if (RoutingInfo != null) { - if (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING != featureId) { + if (TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) { return; } } @@ -2580,13 +2580,13 @@ internal void OnFeatureExtAck(int featureId, byte[] data) break; } - case TdsEnums.FEATUREEXT_AZURESQLDNSCACHING: + case TdsEnums.FEATUREEXT_SQLDNSCACHING: { - SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AZURESQLDNSCACHING", ObjectID); + SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for SQLDNSCACHING", ObjectID); if (data.Length < 1) { - SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for AZURESQLDNSCACHING", ObjectID); + SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for SQLDNSCACHING", ObjectID); throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } @@ -2747,4 +2747,3 @@ internal void SetDerivedNames(string protocol, string serverName) } } } - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs index a5ae822290..66f15bda55 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -214,7 +214,7 @@ public enum EnvChangeType : byte public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; - public const byte FEATUREEXT_AZURESQLDNSCACHING = 0x0B; + public const byte FEATUREEXT_SQLDNSCACHING = 0x0B; [Flags] public enum FeatureExtension : uint @@ -227,7 +227,7 @@ public enum FeatureExtension : uint AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), - AzureSQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING - 1) + SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1) } public const uint UTF8_IN_TDSCOLLATION = 0x4000000; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 81f0a5e8a5..a305f124b7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3107,13 +3107,13 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) bool ret = false; if (_connHandler._cleanSQLDNSCaching) { - ret = SQLDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); } if ( _connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null - && !SQLDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) + && !SQLFallbackDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) { - ret = SQLDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); + ret = SQLFallbackDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); _connHandler.pendingSQLDNSObject = null; } @@ -7830,14 +7830,14 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD return len; } - internal int WriteAzureSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) + internal int WriteSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) { int len = 5; // 1byte = featureID, 4bytes = featureData length if (write) { // Write Feature ID - _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_AZURESQLDNSCACHING); + _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_SQLDNSCACHING); WriteInt(0, _physicalStateObj); // we don't send any data } @@ -8001,9 +8001,9 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures length += WriteUTF8SupportFeatureRequest(false); } - if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + if ((requestedFeatures & TdsEnums.FeatureExtension.SQLDNSCaching) != 0) { - length += WriteAzureSQLDNSCachingFeatureRequest(false); + length += WriteSQLDNSCachingFeatureRequest(false); } length++; // for terminator @@ -8267,9 +8267,9 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures WriteUTF8SupportFeatureRequest(true); } - if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + if ((requestedFeatures & TdsEnums.FeatureExtension.SQLDNSCaching) != 0) { - WriteAzureSQLDNSCachingFeatureRequest(true); + WriteSQLDNSCachingFeatureRequest(true); } _physicalStateObj.WriteByte(0xFF); // terminator diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 5ffe345122..6252d5e887 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -44,7 +44,7 @@ protected override void CreateSessionHandle(TdsParserStateObject physicalConnect SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); SQLDNSInfo cachedDNSInfo; - bool ret = SQLDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, cachedDNSInfo); } @@ -150,7 +150,7 @@ internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSni } SQLDNSInfo cachedDNSInfo; - bool ret = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, cachedDNSInfo); } 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 3718ff1264..cf4ba6e414 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -192,8 +192,8 @@ Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs - - Microsoft\Data\SqlClient\SQLDNSCache.cs + + Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs 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 c1a88447d8..7c447c9d22 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 @@ -147,7 +147,7 @@ sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposa private bool _serverSupportsDNSCaching = false; /// - /// Get or set if AzureSQLDNSCaching FeatureExtAck is supported by the server. + /// Get or set if SQLDNSCaching FeatureExtAck is supported by the server. /// internal bool IsSQLDNSCachingSupported { @@ -181,7 +181,7 @@ internal bool IsSQLDNSRetryEnabled private bool DNSCachingBeforeRedirect = false; /// - /// Get or set if the control ring send redirect token and AzureSQLDNSCaching FeatureExtAck with true + /// Get or set if the control ring send redirect token and SQLDNSCaching FeatureExtAck with true /// internal bool IsDNSCachingBeforeRedirectSupported { @@ -1583,8 +1583,8 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLSupport; } - // The AzureSQLDNSCaching feature is implicitly set - requestedFeatures |= TdsEnums.FeatureExtension.AzureSQLDNSCaching; + // The SQLDNSCaching feature is implicitly set + requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo); } @@ -2869,7 +2869,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) { if (_routingInfo != null) { - if (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING != featureId) { + if (TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) { return; } } @@ -3087,13 +3087,13 @@ internal void OnFeatureExtAck(int featureId, byte[] data) break; } - case TdsEnums.FEATUREEXT_AZURESQLDNSCACHING: + case TdsEnums.FEATUREEXT_SQLDNSCACHING: { - SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for AZURESQLDNSCACHING", ObjectID); + SqlClientEventSource.Log.AdvancedTraceEvent(" {0}, Received feature extension acknowledgement for SQLDNSCACHING", ObjectID); if (data.Length < 1) { - SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for AZURESQLDNSCACHING", ObjectID); + SqlClientEventSource.Log.TraceEvent(" {0}, Unknown token for SQLDNSCACHING", ObjectID); throw SQL.ParsingError(ParsingErrorState.CorruptedTdsStream); } @@ -3255,4 +3255,3 @@ internal void SetDerivedNames(string protocol, string serverName) } } } - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs index 4272af1d31..a6a1e30661 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -206,7 +206,7 @@ internal static class TdsEnums public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; - public const byte FEATUREEXT_AZURESQLDNSCACHING = 0x0B; + public const byte FEATUREEXT_SQLDNSCACHING = 0x0B; [Flags] public enum FeatureExtension : uint @@ -219,7 +219,7 @@ public enum FeatureExtension : uint AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), - AzureSQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_AZURESQLDNSCACHING - 1) + SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1) } public const uint UTF8_IN_TDSCOLLATION = 0x4000000; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index ff0c7bba7a..eceff46048 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3538,13 +3538,13 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) bool ret = false; if (_connHandler._cleanSQLDNSCaching) { - ret = SQLDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); } if ( _connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null - && !SQLDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) + && !SQLFallbackDNSCache.Instance.IsDuplicate(_connHandler.pendingSQLDNSObject)) { - ret = SQLDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); + ret = SQLFallbackDNSCache.Instance.AddDNSInfo(_connHandler.pendingSQLDNSObject); _connHandler.pendingSQLDNSObject = null; } @@ -8620,14 +8620,14 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD return len; } - internal int WriteAzureSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) + internal int WriteSQLDNSCachingFeatureRequest(bool write /* if false just calculates the length */) { int len = 5; // 1byte = featureID, 4bytes = featureData length if (write) { // Write Feature ID - _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_AZURESQLDNSCACHING); + _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_SQLDNSCACHING); WriteInt(0, _physicalStateObj); // we don't send any data } @@ -8822,9 +8822,9 @@ internal void TdsLogin(SqlLogin rec, length += WriteUTF8SupportFeatureRequest(false); } - if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + if ((requestedFeatures & TdsEnums.FeatureExtension.SQLDNSCaching) != 0) { - length += WriteAzureSQLDNSCachingFeatureRequest(false); + length += WriteSQLDNSCachingFeatureRequest(false); } length++; // for terminator @@ -9099,9 +9099,9 @@ internal void TdsLogin(SqlLogin rec, WriteUTF8SupportFeatureRequest(true); } - if ((requestedFeatures & TdsEnums.FeatureExtension.AzureSQLDNSCaching) != 0) + if ((requestedFeatures & TdsEnums.FeatureExtension.SQLDNSCaching) != 0) { - WriteAzureSQLDNSCachingFeatureRequest(true); + WriteSQLDNSCachingFeatureRequest(true); } _physicalStateObj.WriteByte(0xFF); // terminator diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index c7c395650a..09305c3cf4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -296,7 +296,7 @@ internal TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, bo SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); SQLDNSInfo cachedDNSInfo; - bool ret = SQLDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, physicalConnection, cachedDNSInfo); if (_sessionHandle.Status != TdsEnums.SNI_SUCCESS) @@ -850,7 +850,7 @@ internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeo // serverName : serverInfo.ExtendedServerName // may not use this serverName as key SQLDNSInfo cachedDNSInfo; - bool ret = SQLDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, cachedDNSInfo); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs similarity index 90% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs index 49b20562ad..e18b61cee4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLDNSCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs @@ -7,16 +7,16 @@ namespace Microsoft.Data.SqlClient { - internal class SQLDNSCache + internal class SQLFallbackDNSCache { - private static readonly SQLDNSCache _SQLDNSCache = new SQLDNSCache(); + private static readonly SQLFallbackDNSCache _SQLFallbackDNSCache = new SQLFallbackDNSCache(); private static readonly int initialCapacity = 101; // give some prime number here according to MSDN docs. It will be resized if reached capacity. private ConcurrentDictionary DNSInfoCache; // singleton instance - public static SQLDNSCache Instance { get { return _SQLDNSCache; } } + public static SQLFallbackDNSCache Instance { get { return _SQLFallbackDNSCache; } } - private SQLDNSCache() + private SQLFallbackDNSCache() { int level = 4 * Environment.ProcessorCount; DNSInfoCache = new ConcurrentDictionary(concurrencyLevel: level, 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 470356c700..33460acb8d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DNSCachingTest/DNSCachingTest.cs @@ -14,9 +14,9 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class DNSCachingTest { public static Assembly systemData = Assembly.GetAssembly(typeof(SqlConnection)); - public static Type SQLDNSCacheType = systemData.GetType("Microsoft.Data.SqlClient.SQLDNSCache"); + public static Type SQLFallbackDNSCacheType = systemData.GetType("Microsoft.Data.SqlClient.SQLFallbackDNSCache"); public static Type SQLDNSInfoType = systemData.GetType("Microsoft.Data.SqlClient.SQLDNSInfo"); - public static MethodInfo SQLDNSCacheGetDNSInfo = SQLDNSCacheType.GetMethod("GetDNSInfo", BindingFlags.Instance | BindingFlags.NonPublic); + public static MethodInfo SQLFallbackDNSCacheGetDNSInfo = SQLFallbackDNSCacheType.GetMethod("GetDNSInfo", BindingFlags.Instance | BindingFlags.NonPublic); [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsDNSCachingSetup))] @@ -47,7 +47,7 @@ public void DNSCachingGetDNSInfo() connection.Open(); } - var SQLDNSCacheInstance = SQLDNSCacheType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public).GetValue(null); + var SQLFallbackDNSCacheInstance = SQLFallbackDNSCacheType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public).GetValue(null); var serverList = new List>(); serverList.Add(new KeyValuePair(DataTestUtility.DNSCachingServerCR, DataTestUtility.IsDNSCachingSupportedCR)); @@ -61,7 +61,7 @@ public void DNSCachingGetDNSInfo() if (!string.IsNullOrEmpty(server.Key)) { parameters = new object[] { server.Key, null }; - ret = (bool)SQLDNSCacheGetDNSInfo.Invoke(SQLDNSCacheInstance, parameters); + ret = (bool)SQLFallbackDNSCacheGetDNSInfo.Invoke(SQLFallbackDNSCacheInstance, parameters); if (server.Value) { From 439d512dfad913ab3bf7027f7878055295f888f7 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 12 Jun 2020 15:56:31 -0700 Subject: [PATCH 6/9] Do not log to console with LogError --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 3 ++- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 1 + .../src/Microsoft/Data/SqlClient/SqlClientLogger.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 93d77c8b1c..9070b88fa6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -947,8 +947,9 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus SslProtocols protocol = (SslProtocols)protocolVersion; string warningMessage = protocol.GetProtocolWarning(); - if(!string.IsNullOrEmpty(warningMessage)) + if (!string.IsNullOrEmpty(warningMessage)) { + // This logs console warning of insecure protocol in use. _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 4ac3e59e45..4f5fd8a2e1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1296,6 +1296,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod string warningMessage = SslProtocolsHelper.GetProtocolWarning(protocolVersion); if (!string.IsNullOrEmpty(warningMessage)) { + // This logs console warning of insecure protocol in use. _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientLogger.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientLogger.cs index 68afb07e49..5150862224 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientLogger.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientLogger.cs @@ -32,7 +32,6 @@ public void LogWarning(string type, string method, string message) /// public void LogError(string type, string method, string message) { - Console.Out.WriteLine(message); SqlClientEventSource.Log.TraceEvent("{3}", type, method, LogLevel.Error, message); } From c3efb52a23c0da3d5b26e8c71c4e65716705ee89 Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 12 Jun 2020 17:17:39 -0700 Subject: [PATCH 7/9] Add table for supported internal properties --- doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 1eafca2f31..978aaa00db 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -1064,7 +1064,10 @@ GO ## Remarks When this method is called, the values retrieved are those at the current point in time. If you continue using the connection, the values are incorrect. You need to re-execute the method to obtain the most current values. - Supported internal properties: `SQLDNSCachingSupportedState` and `SQLDNSCachingSupportedStateBeforeRedirect`. + |Supported internal properties|Type|Information provided|Return value| + |-----------------------------|---------|----------------------------|------------| + |`SQLDNSCachingSupportedState`|string|To indicate the IsSupported flag sent by the server for DNS Caching|"true", "false", "innerConnection is null!"| + |`SQLDNSCachingSupportedStateBeforeRedirect`|string|To indicate the IsSupported flag sent by the server for DNS Caching before redirection.|"true", "false", "innerConnection is null!"| ]]> From 366b01fc34f95c61390f9aec8431b7f208dcb9aa Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Fri, 12 Jun 2020 17:26:08 -0700 Subject: [PATCH 8/9] Fix typo --- .../src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 70ead347cf..4525d4c75d 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 @@ -2651,7 +2651,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) _cleanSQLDNSCaching = true; } - // need to add more steps for phrase 2 + // need to add more steps for phase 2 // get IPv4 + IPv6 + Port number // not put them in the DNS cache at this point but need to store them somewhere // generate pendingSQLDNSObject and turn on IsSQLDNSRetryEnabled flag From 7b602638a81b248f015341c6a27776bb4f50fe2e Mon Sep 17 00:00:00 2001 From: Karina Zhou Date: Mon, 15 Jun 2020 14:18:15 -0700 Subject: [PATCH 9/9] Update SNI dependency versioin --- tools/props/Versions.props | 4 ++-- tools/specs/Microsoft.Data.SqlClient.nuspec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index e10ec44a73..817a1dbc1a 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -9,7 +9,7 @@ - 2.0.0-preview1.20141.10 + 2.0.0 4.3.1 4.3.0 @@ -24,7 +24,7 @@ 4.7.0 - 2.0.0-preview1.20141.10 + 2.0.0 4.7.0 4.7.0 4.7.0 diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index f6be6bab6b..3e09d83333 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -27,13 +27,13 @@ When using NuGet 3.x this package requires at least version 3.4. sqlclient microsoft.data.sqlclient - + - + @@ -45,7 +45,7 @@ When using NuGet 3.x this package requires at least version 3.4. - + @@ -57,7 +57,7 @@ When using NuGet 3.x this package requires at least version 3.4. - +