internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData) { _physicalStateObj.SetTimeoutSeconds(rec.timeout); Debug.Assert(recoverySessionData == null || (requestedFeatures | TdsEnums.FeatureExtension.SessionRecovery) != 0, "Recovery session data without session recovery feature request"); Debug.Assert(TdsEnums.MAXLEN_HOSTNAME >= rec.hostName.Length, "_workstationId.Length exceeds the max length for this value"); Debug.Assert(rec.userName == null || (rec.userName != null && TdsEnums.MAXLEN_USERNAME >= rec.userName.Length), "_userID.Length exceeds the max length for this value"); Debug.Assert(rec.password == null || (rec.password != null && TdsEnums.MAXLEN_PASSWORD >= rec.password.Length), "_password.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_APPNAME >= rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_SERVERNAME >= rec.serverName.Length, "_dataSource.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_LANGUAGE >= rec.language.Length, "_currentLanguage .Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_DATABASE >= rec.database.Length, "_initialCatalog.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE >= rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value"); Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth); // get the password up front to use in sspi logic below byte[] encryptedPassword = null; int encryptedPasswordLengthInBytes; bool useFeatureExt = (requestedFeatures != TdsEnums.FeatureExtension.None); string userName; { userName = rec.userName; encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password); encryptedPasswordLengthInBytes = encryptedPassword.Length; // password in clear text is already encrypted and its length is in byte } // set the message type _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7; // length in bytes int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN; string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME; Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec"); // add up variable-len portions (multiply by 2 for byte len of char strings) // checked { length += (rec.hostName.Length + rec.applicationName.Length + rec.serverName.Length + clientInterfaceName.Length + rec.language.Length + rec.database.Length + rec.attachDBFilename.Length) * 2; if (useFeatureExt) { length += 4; } } // allocate memory for SSPI variables byte[] outSSPIBuff = null; UInt32 outSSPILength = 0; // only add lengths of password and username if not using SSPI if (!rec.useSSPI) { checked { length += (userName.Length * 2) + encryptedPasswordLengthInBytes ; } } else { if (rec.useSSPI) { // now allocate proper length of buffer, and set length outSSPIBuff = new byte[s_maxSSPILength]; outSSPILength = s_maxSSPILength; // Call helper function for SSPI data and actual length. // Since we don't have SSPI data from the server, send null for the // byte[] buffer and 0 for the int length. Debug.Assert(SniContext.Snix_Login == _physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext)); _physicalStateObj.SniContext = SniContext.Snix_LoginSspi; SSPIData(null, 0, outSSPIBuff, ref outSSPILength); if (outSSPILength > Int32.MaxValue) { throw SQL.InvalidSSPIPacketSize(); // SqlBu 332503 } _physicalStateObj.SniContext = SniContext.Snix_Login; checked { length += (Int32)outSSPILength; } } } int feOffset = length; if (useFeatureExt) { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { length += WriteSessionRecoveryFeatureRequest(recoverySessionData, false); }; length++; // for terminator } try { WriteInt(length, _physicalStateObj); if (recoverySessionData == null) { WriteInt((TdsEnums.DENALI_MAJOR << 24) | (TdsEnums.DENALI_INCREMENT << 16) | TdsEnums.DENALI_MINOR, _physicalStateObj); } else { WriteUnsignedInt(recoverySessionData._tdsVersion, _physicalStateObj); } WriteInt(rec.packetSize, _physicalStateObj); WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj); WriteInt(TdsParserStaticMethods.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj); WriteInt(0, _physicalStateObj); // connectionID is unused // Log7Flags (DWORD) int log7Flags = 0; /* Current snapshot from TDS spec with the offsets added: 0) fByteOrder:1, // byte order of numeric data types on client 1) fCharSet:1, // character set on client 2) fFloat:2, // Type of floating point on client 4) fDumpLoad:1, // Dump/Load and BCP enable 5) fUseDb:1, // USE notification 6) fDatabase:1, // Initial database fatal flag 7) fSetLang:1, // SET LANGUAGE notification 8) fLanguage:1, // Initial language fatal flag 9) fODBC:1, // Set if client is ODBC driver 10) fTranBoundary:1, // Transaction boundary notification 11) fDelegatedSec:1, // Security with delegation is available 12) fUserType:3, // Type of user 15) fIntegratedSecurity:1, // Set if client is using integrated security 16) fSQLType:4, // Type of SQL sent from client 20) fOLEDB:1, // Set if client is OLEDB driver 21) fSpare1:3, // first bit used for read-only intent, rest unused 24) fResetPassword:1, // set if client wants to reset password 25) fNoNBCAndSparse:1, // set if client does not support NBC and Sparse column 26) fUserInstance:1, // This connection wants to connect to a SQL "user instance" 27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly. 28) fExtension:1 // Extensions are used 32 - total */ // first byte log7Flags |= TdsEnums.USE_DB_ON << 5; log7Flags |= TdsEnums.INIT_DB_FATAL << 6; log7Flags |= TdsEnums.SET_LANG_ON << 7; // second byte log7Flags |= TdsEnums.INIT_LANG_FATAL << 8; log7Flags |= TdsEnums.ODBC_ON << 9; if (rec.useReplication) { log7Flags |= TdsEnums.REPL_ON << 12; } if (rec.useSSPI) { log7Flags |= TdsEnums.SSPI_ON << 15; } // third byte if (rec.readOnlyIntent) { log7Flags |= TdsEnums.READONLY_INTENT_ON << 21; // read-only intent flag is a first bit of fSpare1 } // 4th one if (rec.userInstance) { log7Flags |= 1 << 26; } if (useFeatureExt) { log7Flags |= 1 << 28; } WriteInt(log7Flags, _physicalStateObj); WriteInt(0, _physicalStateObj); // ClientTimeZone is not used WriteInt(0, _physicalStateObj); // LCID is unused by server // Start writing offset and length of variable length portions int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN; // write offset/length pairs // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record WriteShort(offset, _physicalStateObj); // host name offset WriteShort(rec.hostName.Length, _physicalStateObj); offset += rec.hostName.Length * 2; // Only send user/password over if not fSSPI... If both user/password and SSPI are in login // rec, only SSPI is used. Confirmed same bahavior as in luxor. if (rec.useSSPI == false) { WriteShort(offset, _physicalStateObj); // userName offset WriteShort(userName.Length, _physicalStateObj); offset += userName.Length * 2; // the encrypted password is a byte array - so length computations different than strings WriteShort(offset, _physicalStateObj); // password offset WriteShort(encryptedPasswordLengthInBytes / 2, _physicalStateObj); offset += encryptedPasswordLengthInBytes; } else { // case where user/password data is not used, send over zeros WriteShort(0, _physicalStateObj); // userName offset WriteShort(0, _physicalStateObj); WriteShort(0, _physicalStateObj); // password offset WriteShort(0, _physicalStateObj); } WriteShort(offset, _physicalStateObj); // app name offset WriteShort(rec.applicationName.Length, _physicalStateObj); offset += rec.applicationName.Length * 2; WriteShort(offset, _physicalStateObj); // server name offset WriteShort(rec.serverName.Length, _physicalStateObj); offset += rec.serverName.Length * 2; WriteShort(offset, _physicalStateObj); if (useFeatureExt) { WriteShort(4, _physicalStateObj); // length of ibFeatgureExtLong (which is a DWORD) offset += 4; } else { WriteShort(0, _physicalStateObj); // ununsed (was remote password ?) } WriteShort(offset, _physicalStateObj); // client interface name offset WriteShort(clientInterfaceName.Length, _physicalStateObj); offset += clientInterfaceName.Length * 2; WriteShort(offset, _physicalStateObj); // language name offset WriteShort(rec.language.Length, _physicalStateObj); offset += rec.language.Length * 2; WriteShort(offset, _physicalStateObj); // database name offset WriteShort(rec.database.Length, _physicalStateObj); offset += rec.database.Length * 2; if (null == s_nicAddress) s_nicAddress = TdsParserStaticMethods.GetNetworkPhysicalAddressForTdsLoginOnly(); _physicalStateObj.WriteByteArray(s_nicAddress, s_nicAddress.Length, 0); WriteShort(offset, _physicalStateObj); // ibSSPI offset if (rec.useSSPI) { WriteShort((int)outSSPILength, _physicalStateObj); offset += (int)outSSPILength; } else { WriteShort(0, _physicalStateObj); } WriteShort(offset, _physicalStateObj); // DB filename offset WriteShort(rec.attachDBFilename.Length, _physicalStateObj); offset += rec.attachDBFilename.Length * 2; WriteShort(offset, _physicalStateObj); // reset password offset WriteShort(0, _physicalStateObj); WriteInt(0, _physicalStateObj); // reserved for chSSPI // write variable length portion WriteString(rec.hostName, _physicalStateObj); // if we are using SSPI, do not send over username/password, since we will use SSPI instead // same behavior as Luxor if (!rec.useSSPI) { WriteString(userName, _physicalStateObj); { _physicalStateObj.WriteByteArray(encryptedPassword, encryptedPasswordLengthInBytes, 0); } } WriteString(rec.applicationName, _physicalStateObj); WriteString(rec.serverName, _physicalStateObj); // write ibFeatureExtLong if (useFeatureExt) { WriteInt(feOffset, _physicalStateObj); } WriteString(clientInterfaceName, _physicalStateObj); WriteString(rec.language, _physicalStateObj); WriteString(rec.database, _physicalStateObj); // send over SSPI data if we are using SSPI if (rec.useSSPI) _physicalStateObj.WriteByteArray(outSSPIBuff, (int)outSSPILength, 0); WriteString(rec.attachDBFilename, _physicalStateObj); if (useFeatureExt) { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { length += WriteSessionRecoveryFeatureRequest(recoverySessionData, true); }; _physicalStateObj.WriteByte(0xFF); // terminator } } catch (Exception e) { if (ADP.IsCatchableExceptionType(e)) { // be sure to wipe out our buffer if we started sending stuff _physicalStateObj._outputPacketNumber = 1; // end of message - reset to 1 - per ramas _physicalStateObj.ResetBuffer(); } throw; } _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); _physicalStateObj._pendingData = true; _physicalStateObj._messageStatus = 0; }// tdsLogin
internal SqlDataReader TdsExecuteTransactionManagerRequest( byte[] buffer, TdsEnums.TransactionManagerRequestType request, string transactionName, TdsEnums.TransactionManagerIsolationLevel isoLevel, int timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj ) { Debug.Assert(this == stateObj.Parser, "different parsers"); if (TdsParserState.Broken == State || TdsParserState.Closed == State) { return null; } // Promote, Commit and Rollback requests for // delegated transactions often happen while there is an open result // set, so we need to handle them by using a different MARS session, // otherwise we'll write on the physical state objects while someone // else is using it. When we don't have MARS enabled, we need to // lock the physical state object to syncronize it's use at least // until we increment the open results count. Once it's been // incremented the delegated transaction requests will fail, so they // won't stomp on anything. Debug.Assert(!_connHandler.ThreadHasParserLockForClose || _connHandler._parserLock.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken"); bool callerHasConnectionLock = _connHandler.ThreadHasParserLockForClose; // If the thread already claims to have the parser lock, then we will let the caller handle releasing it if (!callerHasConnectionLock) { _connHandler._parserLock.Wait(canReleaseFromAnyThread: false); _connHandler.ThreadHasParserLockForClose = true; } // Capture _asyncWrite (after taking lock) to restore it afterwards bool hadAsyncWrites = _asyncWrite; try { // Temprarily disable async writes _asyncWrite = false; stateObj._outputMessageType = TdsEnums.MT_TRANS; // set message type stateObj.SetTimeoutSeconds(timeout); stateObj.SniContext = SniContext.Snix_Execute; const int marsHeaderSize = 18; // 4 + 2 + 8 + 4 const int totalHeaderLength = 22; // 4 + 4 + 2 + 8 + 4 Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length"); // Write total header length WriteInt(totalHeaderLength, stateObj); // Write mars header length WriteInt(marsHeaderSize, stateObj); WriteMarsHeaderData(stateObj, _currentTransaction); WriteShort((short)request, stateObj); // write TransactionManager Request type bool returnReader = false; switch (request) { case TdsEnums.TransactionManagerRequestType.Begin: Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!"); // Only assign the passed in transaction if it is not equal to the current transaction. // And, if it is not equal, the current actually should be null. Anything else // is a unexpected state. The concern here is mainly for the mixed use of // T-SQL and API transactions. // Expected states: // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction // passed in on BeginTransaction API call. // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction // passed in but equivalent to _currentTransaction. // #1 will occur on standard BeginTransactionAPI call. #2 should only occur if // t-sql transaction started followed by a call to SqlConnection.BeginTransaction. // Any other state is unknown. if (_currentTransaction != transaction) { Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point"); PendingTransaction = transaction; } stateObj.WriteByte((byte)isoLevel); stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); break; case TdsEnums.TransactionManagerRequestType.Commit: Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit"); stateObj.WriteByte((byte)0); // No xact name stateObj.WriteByte(0); // No flags Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); // WriteByte((byte) 0, stateObj); // IsolationLevel // WriteByte((byte) 0, stateObj); // No begin xact name break; case TdsEnums.TransactionManagerRequestType.Rollback: stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); stateObj.WriteByte(0); // No flags Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!"); // WriteByte((byte) 0, stateObj); // IsolationLevel // WriteByte((byte) 0, stateObj); // No begin xact name break; case TdsEnums.TransactionManagerRequestType.Save: stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string). WriteString(transactionName, stateObj); break; default: Debug.Assert(false, "Unexpected TransactionManagerRequest"); break; } Task writeTask = stateObj.WritePacket(TdsEnums.HARDFLUSH); Debug.Assert(writeTask == null, "Writes should not pend when writing sync"); stateObj._pendingData = true; stateObj._messageStatus = 0; SqlDataReader dtcReader = null; stateObj.SniContext = SniContext.Snix_Read; if (returnReader) { dtcReader = new SqlDataReader(null, CommandBehavior.Default); Debug.Assert(this == stateObj.Parser, "different parser"); #if DEBUG // Remove the current owner of stateObj - otherwise we will hit asserts stateObj.Owner = null; #endif dtcReader.Bind(stateObj); // force consumption of metadata _SqlMetaDataSet metaData = dtcReader.MetaData; } else { Run(RunBehavior.UntilDone, null, null, null, stateObj); } return dtcReader; } catch (Exception e) { if (!ADP.IsCatchableExceptionType(e)) { throw; } FailureCleanup(stateObj, e); throw; } finally { // SQLHotfix 50000518 // make sure we don't leave temporary fields set when leaving this function _pendingTransaction = null; _asyncWrite = hadAsyncWrites; if (!callerHasConnectionLock) { _connHandler.ThreadHasParserLockForClose = false; _connHandler._parserLock.Release(); } } }
internal SqlDataReader TdsExecuteTransactionManagerRequest(byte[] buffer, TdsEnums.TransactionManagerRequestType request, string transactionName, TdsEnums.TransactionManagerIsolationLevel isoLevel, int timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, bool isDelegateControlRequest) { SqlDataReader reader2; if ((TdsParserState.Broken == this.State) || (this.State == TdsParserState.Closed)) { return null; } bool lockTaken = false; lock (this._connHandler) { try { try { if (this._isYukon && !this.MARSOn) { Monitor.Enter(this._physicalStateObj, ref lockTaken); } if (!isDelegateControlRequest) { this._connHandler.CheckEnlistedTransactionBinding(); } stateObj._outputMessageType = 14; stateObj.SetTimeoutSeconds(timeout); stateObj.SniContext = SniContext.Snix_Execute; if (this._isYukon) { this.WriteMarsHeader(stateObj, this._currentTransaction); } this.WriteShort((short) request, stateObj); bool flag = false; switch (request) { case TdsEnums.TransactionManagerRequestType.GetDTCAddress: this.WriteShort(0, stateObj); flag = true; goto Label_0193; case TdsEnums.TransactionManagerRequestType.Propagate: if (buffer == null) { break; } this.WriteShort(buffer.Length, stateObj); this.WriteByteArray(buffer, buffer.Length, 0, stateObj); goto Label_0193; case TdsEnums.TransactionManagerRequestType.Begin: if (this._currentTransaction != transaction) { this.PendingTransaction = transaction; } this.WriteByte((byte) isoLevel, stateObj); this.WriteByte((byte) (transactionName.Length * 2), stateObj); this.WriteString(transactionName, stateObj); goto Label_0193; case TdsEnums.TransactionManagerRequestType.Commit: this.WriteByte(0, stateObj); this.WriteByte(0, stateObj); goto Label_0193; case TdsEnums.TransactionManagerRequestType.Rollback: this.WriteByte((byte) (transactionName.Length * 2), stateObj); this.WriteString(transactionName, stateObj); this.WriteByte(0, stateObj); goto Label_0193; case TdsEnums.TransactionManagerRequestType.Save: this.WriteByte((byte) (transactionName.Length * 2), stateObj); this.WriteString(transactionName, stateObj); goto Label_0193; default: goto Label_0193; } this.WriteShort(0, stateObj); Label_0193: stateObj.WritePacket(1); stateObj._pendingData = true; SqlDataReader reader = null; stateObj.SniContext = SniContext.Snix_Read; if (flag) { reader = new SqlDataReader(null, CommandBehavior.Default); reader.Bind(stateObj); _SqlMetaDataSet metaData = reader.MetaData; } else { this.Run(RunBehavior.UntilDone, null, null, null, stateObj); } if ((request == TdsEnums.TransactionManagerRequestType.Begin) || (request == TdsEnums.TransactionManagerRequestType.Propagate)) { if ((transaction != null) && (transaction.TransactionId == this._retainedTransactionId)) { return reader; } this._retainedTransactionId = 0L; } return reader; } catch (Exception exception) { if (!ADP.IsCatchableExceptionType(exception)) { throw; } this.FailureCleanup(stateObj, exception); throw; } return reader2; } finally { this._pendingTransaction = null; if (lockTaken) { Monitor.Exit(this._physicalStateObj); } } } return reader2; }