System.Data.SqlClient.TdsParser.ConsumePreLoginHandshake C# (CSharp) Method

ConsumePreLoginHandshake() private method

private ConsumePreLoginHandshake ( bool encrypt, bool trustServerCert, bool integratedSecurity, bool &marsCapable ) : PreLoginHandshakeStatus
encrypt bool
trustServerCert bool
integratedSecurity bool
marsCapable bool
return PreLoginHandshakeStatus
        private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, bool integratedSecurity, out bool marsCapable)
        {
            marsCapable = _fMARS; // Assign default value

            bool isYukonOrLater = false;
            Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
            bool result = _physicalStateObj.TryReadNetworkPacket();
            if (!result) { throw SQL.SynchronousCallMayNotPend(); }

            if (_physicalStateObj._inBytesRead == 0)
            {
                // If the server did not respond then something has gone wrong and we need to close the connection
                _physicalStateObj.AddError(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.PreloginError(), "", 0));
                _physicalStateObj.Dispose();
                ThrowExceptionAndWarning(_physicalStateObj);
            }

            if (!_physicalStateObj.TryProcessHeader()) { throw SQL.SynchronousCallMayNotPend(); }

            if(_physicalStateObj._inBytesPacket > TdsEnums.MAX_PACKET_SIZE || _physicalStateObj._inBytesPacket < 0)
            {
                throw SQL.InvalidPacketSize();
            }
            byte[] payload = new byte[_physicalStateObj._inBytesPacket];

            Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
            result = _physicalStateObj.TryReadByteArray(payload, 0, payload.Length);
            if (!result) { throw SQL.SynchronousCallMayNotPend(); }

            if (payload[0] == 0xaa)
            {
                // If the first byte is 0xAA, we are connecting to a 6.5 or earlier server, which
                // is not supported. 
                throw SQL.InvalidSQLServerVersionUnknown();
            }

            int offset = 0;
            int payloadOffset = 0;
            int payloadLength = 0;
            int option = payload[offset++];

            while (option != (byte)PreLoginOptions.LASTOPT)
            {
                switch (option)
                {
                    case (int)PreLoginOptions.VERSION:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];

                        byte majorVersion = payload[payloadOffset];
                        byte minorVersion = payload[payloadOffset + 1];
                        int level = (payload[payloadOffset + 2] << 8) |
                                             payload[payloadOffset + 3];

                        isYukonOrLater = majorVersion >= 9;
                        if (!isYukonOrLater)
                        {
                            marsCapable = false;            // If pre-Yukon, MARS not supported.
                        }

                        break;

                    case (int)PreLoginOptions.ENCRYPT:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];

                        EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];

                        /* internal enum EncryptionOptions {
                            OFF,
                            ON,
                            NOT_SUP,
                            REQ,
                            LOGIN
                        } */

                        switch (_encryptionOption)
                        {
                            case (EncryptionOptions.ON):
                                if (serverOption == EncryptionOptions.NOT_SUP)
                                {
                                    _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
                                    _physicalStateObj.Dispose();
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                }

                                break;

                            case (EncryptionOptions.OFF):
                                if (serverOption == EncryptionOptions.OFF)
                                {
                                    // Only encrypt login.
                                    _encryptionOption = EncryptionOptions.LOGIN;
                                }
                                else if (serverOption == EncryptionOptions.REQ)
                                {
                                    // Encrypt all.
                                    _encryptionOption = EncryptionOptions.ON;
                                }

                                break;

                            case (EncryptionOptions.NOT_SUP):
                                if (serverOption == EncryptionOptions.REQ)
                                {
                                    _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
                                    _physicalStateObj.Dispose();
                                    ThrowExceptionAndWarning(_physicalStateObj);
                                }

                                break;

                            default:
                                Debug.Assert(false, "Invalid client encryption option detected");
                                break;
                        }

                        if (_encryptionOption == EncryptionOptions.ON ||
                            _encryptionOption == EncryptionOptions.LOGIN)
                        {
                            UInt32 error = 0;
                            UInt32 info = ((encrypt && !trustServerCert) ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
                                | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);

#if MANAGED_SNI
                            error = SNIProxy.Singleton.EnableSsl(_physicalStateObj.Handle, info);
#else

                            if (encrypt && !integratedSecurity)
                            {
                                // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI 
                                // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context.
                                info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS;
                            }

                            // Add SSL (Encryption) SNI provider.
                            error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info);
#endif // MANAGED_SNI

                            if (error != TdsEnums.SNI_SUCCESS)
                            {
                                _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
                                ThrowExceptionAndWarning(_physicalStateObj);
                            }

#if !MANAGED_SNI
                            // in the case where an async connection is made, encryption is used and Windows Authentication is used, 
                            // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its 
                            // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete 
                            // before calling SNISecGenClientContext).
                            error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining());
                            if (error != TdsEnums.SNI_SUCCESS)
                            {
                                _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj));
                                ThrowExceptionAndWarning(_physicalStateObj);
                            }
#endif

                            // create a new packet encryption changes the internal packet size
                            try { } // EmptyTry/Finally to avoid FXCop violation
                            finally
                            {
                                _physicalStateObj.ClearAllWritePackets();
                            }
                        }

                        break;

                    case (int)PreLoginOptions.INSTANCE:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];

                        byte ERROR_INST = 0x1;
                        byte instanceResult = payload[payloadOffset];

                        if (instanceResult == ERROR_INST)
                        {
                            // Check if server says ERROR_INST. That either means the cached info
                            // we used to connect is not valid or we connected to a named instance
                            // listening on default params.
                            return PreLoginHandshakeStatus.InstanceFailure;
                        }

                        break;

                    case (int)PreLoginOptions.THREADID:
                        // DO NOTHING FOR THREADID
                        offset += 4;
                        break;

                    case (int)PreLoginOptions.MARS:
                        payloadOffset = payload[offset++] << 8 | payload[offset++];
                        payloadLength = payload[offset++] << 8 | payload[offset++];

                        marsCapable = (payload[payloadOffset] == 0 ? false : true);

                        Debug.Assert(payload[payloadOffset] == 0 || payload[payloadOffset] == 1, "Value for Mars PreLoginHandshake option not equal to 1 or 0!");
                        break;

                    case (int)PreLoginOptions.TRACEID:
                        // DO NOTHING FOR TRACEID
                        offset += 4;
                        break;

                    default:
                        Debug.Assert(false, "UNKNOWN option in ConsumePreLoginHandshake, option:" + option);

                        // DO NOTHING FOR THESE UNKNOWN OPTIONS
                        offset += 4;

                        break;
                }

                if (offset < payload.Length)
                {
                    option = payload[offset++];
                }
                else
                {
                    break;
                }
            }

            return PreLoginHandshakeStatus.Successful;
        }
TdsParser