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 indicates 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 behavior 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); // unused (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