コード例 #1
0
ファイル: SNITcpHandle.cs プロジェクト: vikas304/SqlClient
        // 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;
            Task <Socket> connectTask;

            Task <IPAddress[]> 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;
                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} serverAddresses.Length {1} Exception: {2}", args0: _connectionId, args1: serverAddresses.Length, args2: Strings.SNI_ERROR_47);
                ReportTcpSNIError(0, SNICommon.MultiSubnetFailoverWithMoreThan64IPs, Strings.SNI_ERROR_47);
                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;
                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} Connection timed out, Exception: {1}", args0: _connectionId, args1: Strings.SNI_ERROR_40);
                ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_40);
                return(availableSocket);
            }

            availableSocket = connectTask.Result;
            return(availableSocket);
        }
コード例 #2
0
        // 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;
            Task <Socket> connectTask;

            Task <IPAddress[]> 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);
        }
コード例 #3
0
        /// <summary>
        /// Creates an SNITCPHandle object
        /// </summary>
        /// <param name="details">Data source</param>
        /// <param name="timerExpire">Timer expiration</param>
        /// <param name="parallel">Should MultiSubnetFailover be used</param>
        /// <param name="cachedFQDN">Key for DNS Cache</param>
        /// <param name="pendingDNSInfo">Used for DNS Cache</param>
        /// <returns>SNITCPHandle</returns>
        private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo)
        {
            // TCP Format:
            // tcp:<host name>\<instance name>
            // tcp:<host name>,<TCP/IP port number>

            string hostName = details.ServerName;

            if (string.IsNullOrWhiteSpace(hostName))
            {
                SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.InvalidConnStringError, Strings.SNI_ERROR_25);
                return(null);
            }

            int  port = -1;
            bool isAdminConnection = details._connectionProtocol == DataSource.Protocol.Admin;

            if (details.IsSsrpRequired)
            {
                try
                {
                    port = isAdminConnection ?
                           SSRP.GetDacPortByInstanceName(hostName, details.InstanceName) :
                           SSRP.GetPortByInstanceName(hostName, details.InstanceName);
                }
                catch (SocketException se)
                {
                    SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.InvalidConnStringError, se);
                    return(null);
                }
            }
            else if (details.Port != -1)
            {
                port = details.Port;
            }
            else
            {
                port = isAdminConnection ? DefaultSqlServerDacPort : DefaultSqlServerPort;
            }

            return(new SNITCPHandle(hostName, port, timerExpire, parallel, cachedFQDN, ref pendingDNSInfo));
        }
コード例 #4
0
        /// <summary>
        /// Create a SNI connection handle
        /// </summary>
        /// <param name="fullServerName">Full server name from connection string</param>
        /// <param name="ignoreSniOpenTimeout">Ignore open timeout</param>
        /// <param name="timerExpire">Timer expiration</param>
        /// <param name="instanceName">Instance name</param>
        /// <param name="spnBuffer">SPN</param>
        /// <param name="flushCache">Flush packet cache</param>
        /// <param name="async">Asynchronous connection</param>
        /// <param name="parallel">Attempt parallel connects</param>
        /// <param name="isIntegratedSecurity"></param>
        /// <param name="cachedFQDN">Used for DNS Cache</param>
        /// <param name="pendingDNSInfo">Used for DNS Cache</param>
        /// <returns>SNI handle</returns>
        internal SNIHandle CreateConnectionHandle(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];

            bool   errorWithLocalDBProcessing;
            string localDBDataSource = GetLocalDBDataSource(fullServerName, out errorWithLocalDBProcessing);

            if (errorWithLocalDBProcessing)
            {
                return(null);
            }

            // If a localDB Data source is available, we need to use it.
            fullServerName = localDBDataSource ?? fullServerName;

            DataSource details = DataSource.ParseServerName(fullServerName);

            if (details == null)
            {
                return(null);
            }

            SNIHandle sniHandle = null;

            switch (details._connectionProtocol)
            {
            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, parallel, cachedFQDN, ref pendingDNSInfo);
                break;

            case DataSource.Protocol.NP:
                sniHandle = CreateNpHandle(details, timerExpire, parallel);
                break;

            default:
                Debug.Fail($"Unexpected connection protocol: {details._connectionProtocol}");
                break;
            }

            if (isIntegratedSecurity)
            {
                try
                {
                    spnBuffer = GetSqlServerSPN(details);
                }
                catch (Exception e)
                {
                    SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, SNICommon.ErrorSpnLookup, e);
                }
            }

            return(sniHandle);
        }
コード例 #5
0
        // 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);

            string IPv4String = null;
            string IPv6String = null;

            IPAddress serverIPv4 = null;
            IPAddress serverIPv6 = null;

            foreach (IPAddress ipAddress in ipAddresses)
            {
                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()
            {
                for (int i = 0; i < sockets.Length; ++i)
                {
                    try
                    {
                        if (sockets[i] != null && !sockets[i].Connected)
                        {
                            sockets[i].Dispose();
                            sockets[i] = null;
                        }
                    }
                    catch { }
                }
            }

            if (!isInfiniteTimeout)
            {
                cts = new CancellationTokenSource(timeout);
                cts.Token.Register(Cancel);
            }

            Socket availableSocket = null;

            try
            {
                for (int i = 0; i < sockets.Length; ++i)
                {
                    try
                    {
                        if (ipAddresses[i] != null)
                        {
                            sockets[i] = new Socket(ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                            sockets[i].Connect(ipAddresses[i], port);
                            if (sockets[i] != null) // sockets[i] can be null if cancel callback is executed during connect()
                            {
                                if (sockets[i].Connected)
                                {
                                    availableSocket = sockets[i];
                                    break;
                                }
                                else
                                {
                                    sockets[i].Dispose();
                                    sockets[i] = null;
                                }
                            }
                        }
                    }
                    catch { }
                }
            }
            finally
            {
                cts?.Dispose();
            }

            return(availableSocket);
        }
コード例 #6
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="serverName">Server name</param>
        /// <param name="port">TCP port number</param>
        /// <param name="timerExpire">Connection timer expiration</param>
        /// <param name="callbackObject">Callback object</param>
        /// <param name="parallel">Parallel executions</param>
        /// <param name="cachedFQDN">Key for DNS Cache</param>
        /// <param name="pendingDNSInfo">Used for DNS Cache</param>
        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 = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo);

            try
            {
                TimeSpan ts = default(TimeSpan);

                // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count
                // The infinite Timeout is a function of ConnectionString Timeout=0
                bool isInfiniteTimeOut = long.MaxValue == timerExpire;
                if (!isInfiniteTimeOut)
                {
                    ts = DateTime.FromFileTime(timerExpire) - DateTime.Now;
                    ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts;
                }

                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)
                    {
                        _socket = TryConnectParallel(serverName, port, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo);
                    }
                    else
                    {
                        _socket = Connect(serverName, port, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo);
                    }
                }
                catch (Exception ex)
                {
                    // 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)
                {
                    if (_socket != null)
                    {
                        _socket.Dispose();
                        _socket = null;
                    }

                    if (reportError)
                    {
                        ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty);
                    }
                    return;
                }

                _socket.NoDelay = true;
                _tcpStream      = new SNINetworkStream(_socket, true);

                _sslOverTdsStream = new SslOverTdsStream(_tcpStream);
                _sslStream        = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate));
            }
            catch (SocketException se)
            {
                ReportTcpSNIError(se);
                return;
            }
            catch (Exception e)
            {
                ReportTcpSNIError(e);
                return;
            }

            _stream = _tcpStream;
            _status = TdsEnums.SNI_SUCCESS;
        }
コード例 #7
0
 // 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
 }
コード例 #8
0
 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.GetInstance().CreateConnectionHandle(this, serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, cachedFQDN, ref pendingDNSInfo);
     if (_sessionHandle == null)
     {
         _parser.ProcessSNIError(this);
     }
     else if (async)
     {
         // Create call backs and allocate to the session handle
         _sessionHandle.SetAsyncCallbacks(ReadAsyncCallback, WriteAsyncCallback);
     }
 }
コード例 #9
0
 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.GetInstance().CreateConnectionHandle(serverName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref spnBuffer, flushCache, async, parallel, isIntegratedSecurity, cachedFQDN, ref pendingDNSInfo);
     if (_sessionHandle == null)
     {
         _parser.ProcessSNIError(this);
     }
     else
     {
         SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.CreatePhysicalSNIHandle | Info | State Object Id {0}, Session Id {1}, ServerName {2}, Async = {3}", _objectID, _sessionHandle?.ConnectionId, serverName, async);
         if (async)
         {
             // Create call backs and allocate to the session handle
             _sessionHandle.SetAsyncCallbacks(ReadAsyncCallback, WriteAsyncCallback);
         }
     }
 }
コード例 #10
0
ファイル: SNITcpHandle.cs プロジェクト: vikas304/SqlClient
        // 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);

            string IPv4String = null;
            string IPv6String = null;

            IPAddress serverIPv4 = null;
            IPAddress serverIPv6 = null;

            foreach (IPAddress ipAddress in ipAddresses)
            {
                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()
            {
                for (int i = 0; i < sockets.Length; ++i)
                {
                    try
                    {
                        if (sockets[i] != null && !sockets[i].Connected)
                        {
                            sockets[i].Dispose();
                            sockets[i] = null;
                        }
                    }
                    catch (Exception e)
                    {
                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message);
                    }
                }
            }

            if (!isInfiniteTimeout)
            {
                cts = new CancellationTokenSource(timeout);
                cts.Token.Register(Cancel);
            }

            Socket availableSocket = null;

            try
            {
                for (int i = 0; i < sockets.Length; ++i)
                {
                    try
                    {
                        if (ipAddresses[i] != null)
                        {
                            sockets[i] = new Socket(ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                            // enable keep-alive on socket
                            SetKeepAliveValues(ref sockets[i]);

                            SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connecting to IP address {0} and port {1}", args0: ipAddresses[i], args1: port);
                            sockets[i].Connect(ipAddresses[i], port);
                            if (sockets[i] != null) // sockets[i] can be null if cancel callback is executed during connect()
                            {
                                if (sockets[i].Connected)
                                {
                                    availableSocket = sockets[i];
                                    break;
                                }
                                else
                                {
                                    sockets[i].Dispose();
                                    sockets[i] = null;
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message);
                    }
                }
            }
            finally
            {
                cts?.Dispose();
            }

            return(availableSocket);
        }
コード例 #11
0
ファイル: SNITcpHandle.cs プロジェクト: vikas304/SqlClient
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="serverName">Server name</param>
        /// <param name="port">TCP port number</param>
        /// <param name="timerExpire">Connection timer expiration</param>
        /// <param name="parallel">Parallel executions</param>
        /// <param name="cachedFQDN">Key for DNS Cache</param>
        /// <param name="pendingDNSInfo">Used for DNS Cache</param>
        public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo)
        {
            long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className);

            SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Setting server name = {1}", args0: _connectionId, args1: serverName);
            try
            {
                _targetServer = serverName;
                _sendSync     = new object();

                SQLDNSInfo cachedDNSInfo;
                bool       hasCachedDNSInfo = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo);

                try
                {
                    TimeSpan ts = default(TimeSpan);

                    // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count
                    // The infinite Timeout is a function of ConnectionString Timeout=0
                    bool isInfiniteTimeOut = long.MaxValue == timerExpire;
                    if (!isInfiniteTimeOut)
                    {
                        ts = DateTime.FromFileTime(timerExpire) - DateTime.Now;
                        ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts;
                    }

                    bool reportError = true;

                    SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Connecting to serverName {1} and port {2}", args0: _connectionId, args1: serverName, args2: port);
                    // 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)
                        {
                            _socket = TryConnectParallel(serverName, port, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo);
                        }
                        else
                        {
                            _socket = Connect(serverName, port, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo);
                        }
                    }
                    catch (Exception ex)
                    {
                        // Retry with cached IP address
                        if (ex is SocketException || ex is ArgumentException || ex is AggregateException)
                        {
                            if (hasCachedDNSInfo == false)
                            {
                                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0}, Cached DNS Info not found, exception occurred thrown: {1}", args0: _connectionId, args1: ex?.Message);
                                throw;
                            }
                            else
                            {
                                int portRetry = string.IsNullOrEmpty(cachedDNSInfo.Port) ? port : int.Parse(cachedDNSInfo.Port);
                                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Retrying with cached DNS IP Address {1} and port {2}", args0: _connectionId, args1: cachedDNSInfo.AddrIPv4, args2: 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)
                                    {
                                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0}, Retrying exception {1}", args0: _connectionId, args1: exRetry?.Message);
                                        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
                                    {
                                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0}, Retry failed, exception occurred: {1}", args0: _connectionId, args1: exRetry?.Message);
                                        throw;
                                    }
                                }
                            }
                        }
                        else
                        {
                            throw;
                        }
                    }

                    if (_socket == null || !_socket.Connected)
                    {
                        if (_socket != null)
                        {
                            _socket.Dispose();
                            _socket = null;
                        }

                        if (reportError)
                        {
                            SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} could not be opened, exception occurred: {1}", args0: _connectionId, args1: Strings.SNI_ERROR_40);
                            ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_40);
                        }
                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} Socket could not be opened.", args0: _connectionId);
                        return;
                    }

                    _socket.NoDelay = true;
                    _tcpStream      = new SNINetworkStream(_socket, true);

                    _sslOverTdsStream = new SslOverTdsStream(_tcpStream, _connectionId);
                    _sslStream        = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate));
                }
                catch (SocketException se)
                {
                    SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} Socket exception occurred: Error Code {1}, Message {2}", args0: _connectionId, args1: se?.SocketErrorCode, args2: se?.Message);
                    ReportTcpSNIError(se);
                    return;
                }
                catch (Exception e)
                {
                    SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Connection Id {0} Exception occurred: {1}", args0: _connectionId, args1: e?.Message);
                    ReportTcpSNIError(e);
                    return;
                }

                _stream = _tcpStream;
                _status = TdsEnums.SNI_SUCCESS;
                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connection Id {0} Socket opened successfully, TCP stream ready.", args0: _connectionId);
            }
            finally
            {
                SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID);
            }
        }
コード例 #12
0
ファイル: SNITcpHandle.cs プロジェクト: johnnypham/SqlClient
        // 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, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo)
        {
            SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "IP preference : {0}", Enum.GetName(typeof(SqlConnectionIPAddressPreference), ipPreference));

            IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName);

            string IPv4String = null;
            string IPv6String = null;

            // Returning null socket is handled by the caller function.
            if (ipAddresses == null || ipAddresses.Length == 0)
            {
                return(null);
            }

            Socket[]        sockets            = new Socket[ipAddresses.Length];
            AddressFamily[] preferedIPFamilies = new AddressFamily[2];

            if (ipPreference == SqlConnectionIPAddressPreference.IPv4First)
            {
                preferedIPFamilies[0] = AddressFamily.InterNetwork;
                preferedIPFamilies[1] = AddressFamily.InterNetworkV6;
            }
            else if (ipPreference == SqlConnectionIPAddressPreference.IPv6First)
            {
                preferedIPFamilies[0] = AddressFamily.InterNetworkV6;
                preferedIPFamilies[1] = AddressFamily.InterNetwork;
            }
            // else -> UsePlatformDefault

            CancellationTokenSource cts = null;

            if (!isInfiniteTimeout)
            {
                cts = new CancellationTokenSource(timeout);
                cts.Token.Register(Cancel);
            }

            Socket availableSocket = null;

            try
            {
                // We go through the IP list twice.
                // In the first traversal, we only try to connect with the preferedIPFamilies[0].
                // In the second traversal, we only try to connect with the preferedIPFamilies[1].
                // For UsePlatformDefault preference, we do traversal once.
                for (int i = 0; i < preferedIPFamilies.Length; ++i)
                {
                    for (int n = 0; n < ipAddresses.Length; n++)
                    {
                        IPAddress ipAddress = ipAddresses[n];
                        try
                        {
                            if (ipAddress != null)
                            {
                                if (ipAddress.AddressFamily != preferedIPFamilies[i] && ipPreference != SqlConnectionIPAddressPreference.UsePlatformDefault)
                                {
                                    continue;
                                }

                                sockets[n] = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                                // enable keep-alive on socket
                                SetKeepAliveValues(ref sockets[n]);

                                SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Connecting to IP address {0} and port {1} using {2} address family.",
                                                                          args0: ipAddress,
                                                                          args1: port,
                                                                          args2: ipAddress.AddressFamily);
                                sockets[n].Connect(ipAddress, port);
                                if (sockets[n] != null) // sockets[n] can be null if cancel callback is executed during connect()
                                {
                                    if (sockets[n].Connected)
                                    {
                                        availableSocket = sockets[n];
                                        if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
                                        {
                                            IPv4String = ipAddress.ToString();
                                        }
                                        else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
                                        {
                                            IPv6String = ipAddress.ToString();
                                        }

                                        break;
                                    }
                                    else
                                    {
                                        sockets[n].Dispose();
                                        sockets[n] = null;
                                    }
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message);
                            SqlClientEventSource.Log.TryAdvancedTraceEvent($"{s_className}.{System.Reflection.MethodBase.GetCurrentMethod().Name}{EventType.ERR}THIS EXCEPTION IS BEING SWALLOWED: {e}");
                        }
                    }

                    // If we have already got a valid Socket, or the platform default was prefered
                    // we won't do the second traversal.
                    if (availableSocket is not null || ipPreference == SqlConnectionIPAddressPreference.UsePlatformDefault)
                    {
                        break;
                    }
                }
            }
            finally
            {
                cts?.Dispose();
            }

            // we only record the ip we can connect with successfully.
            if (IPv4String != null || IPv6String != null)
            {
                pendingDNSInfo = new SQLDNSInfo(cachedFQDN, IPv4String, IPv6String, port.ToString());
            }

            return(availableSocket);

            void Cancel()
            {
                for (int i = 0; i < sockets.Length; ++i)
                {
                    try
                    {
                        if (sockets[i] != null && !sockets[i].Connected)
                        {
                            sockets[i].Dispose();
                            sockets[i] = null;
                        }
                    }
                    catch (Exception e)
                    {
                        SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message);
                    }
                }
            }
        }