private static void WriteSessionRecoveryFeatureRequest(TdsPackageWriter writer, SessionData reconnectData) { var(_, initialLength, currentLength, writeState) = SessionRecoveryFeatureRequestLengths(reconnectData); writer.WriteByte(TdsEnums.FEATUREEXT_SRECOVERY); if (reconnectData == null) { writer.WriteInt32(0); } else { writer.WriteInt32(8 + initialLength + currentLength); // length of data w/o total length (initial + current + 2 * sizeof(DWORD)) writer.WriteInt32(initialLength); writer.WriteByteLenString(reconnectData.InitialDatabase); writer.WriteCollation(reconnectData.InitialCollation); writer.WriteByteLenString(reconnectData.InitialLanguage); for (var i = 0; i < SessionData.MaxNumberOfSessionStates; i++) { if (reconnectData.InitialState[i] != null) { writer.WriteByte((byte)i); if (reconnectData.InitialState[i].Length < 0xFF) { writer.WriteByte((byte)reconnectData.InitialState[i].Length); } else { writer.WriteByte(0xFF); writer.WriteInt32(reconnectData.InitialState[i].Length); } writer.WriteByteArray(reconnectData.InitialState[i]); } } writer.WriteInt32(currentLength); writer.WriteByteLenString(reconnectData.Database != reconnectData.InitialDatabase ? reconnectData.Database : null); writer.WriteCollation(SqlCollations.AreSame(reconnectData.InitialCollation, reconnectData.Collation) ? null : reconnectData.Collation); writer.WriteByteLenString(reconnectData.Language != reconnectData.InitialLanguage ? reconnectData.Language : null); for (var i = 0; i < SessionData.MaxNumberOfSessionStates; i++) { if (writeState[i]) { writer.WriteByte((byte)i); if (reconnectData.Delta[i].DataLength < 0xFF) { writer.WriteByte((byte)reconnectData.Delta[i].DataLength); } else { writer.WriteByte(0xFF); writer.WriteInt32(reconnectData.Delta[i].DataLength); } writer.WriteByteArray(reconnectData.Delta[i].Data); } } } }
private static void WriteOptions(TdsPackageWriter writer, string instanceName, bool marsOn) { for (var option = (int)PreLoginOptions.VERSION; option < (int)PreLoginOptions.NUMOPT; option++) { switch (option) { case (int)PreLoginOptions.VERSION: // Major and minor writer.WriteByte(0); writer.WriteByte(0); // Build (Big Endian) writer.WriteByte(0); writer.WriteByte(0); // Sub-build (Little Endian) writer.WriteByte(0); writer.WriteByte(0); break; case (int)PreLoginOptions.ENCRYPT: writer.WriteByte((byte)EncryptionOptions.NOT_SUP); break; case (int)PreLoginOptions.INSTANCE: foreach (var c in instanceName) { writer.WriteByte(c); } writer.WriteByte(0); // null terminate break; case (int)PreLoginOptions.THREADID: writer.WriteUInt32(0); break; case (int)PreLoginOptions.MARS: writer.WriteByte((byte)(marsOn ? 1 : 0)); // null terminate break; case (int)PreLoginOptions.TRACEID: writer.WriteByteArray(new byte[GuidSize]); writer.WriteByteArray(new byte[GuidSize]); writer.WriteUInt32(0); break; } } }
public static void SendTdsLogin(this TdsPackageWriter writer, LoginOptions rec, SessionData recoverySessionData) { const string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME; var requestedFeatures = rec.RequestedFeatures; if (recoverySessionData != null && (requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) == 0) { throw new Exception("Recovery session data without session recovery feature request"); } if (rec.HostName.Length > TdsEnums.MAXLEN_HOSTNAME) { throw new Exception("_workstationId.Length exceeds the max length for this value"); } if (!rec.UseSspi && rec.UserName.Length > TdsEnums.MAXLEN_USERNAME) { throw new Exception("_userID.Length exceeds the max length for this value"); } if (!rec.UseSspi && rec.Password.Length > TdsEnums.MAXLEN_PASSWORD) { throw new Exception("_password.Length exceeds the max length for this value"); } if (rec.ApplicationName.Length > TdsEnums.MAXLEN_APPNAME) { throw new Exception("_applicationName.Length exceeds the max length for this value"); } if (rec.ServerName.Length > TdsEnums.MAXLEN_SERVERNAME) { throw new Exception("_dataSource.Length exceeds the max length for this value"); } if (rec.Language.Length > TdsEnums.MAXLEN_LANGUAGE) { throw new Exception("_currentLanguage .Length exceeds the max length for this value"); } if (rec.Database.Length > TdsEnums.MAXLEN_DATABASE) { throw new Exception("_initialCatalog.Length exceeds the max length for this value"); } if (rec.AttachDbFilename.Length > TdsEnums.MAXLEN_ATTACHDBFILE) { throw new Exception("_attachDBFileName.Length exceeds the max length for this value"); } if (clientInterfaceName.Length > TdsEnums.MAXLEN_CLIENTINTERFACE) { throw new Exception("cchCltIntName can specify at most 128 unicode characters. See Tds spec"); } // get the password up front to use in sspi logic below var useFeatureExt = requestedFeatures != TdsEnums.FeatureExtension.None; // set the message type // length in bytes var length = TdsEnums.YUKON_LOG_REC_FIXED_LEN; // add up variable-len portions (multiply by 2 for byte len of char strings) // 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 // only add lengths of password and username if not using SSPI var userName = ""; var encryptedPassword = new byte[0]; if (rec.UseSspi) { length += rec.ClientToken.Length; } else { userName = rec.UserName; encryptedPassword = ObfuscatePassword(rec.Password); length += userName.Length * 2 + encryptedPassword.Length; } var feOffset = length; if (useFeatureExt) { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { length += SessionRecoveryFeatureRequestLengths(recoverySessionData).totalLength; } if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) { length += 5; } length++; // for terminator } writer.NewPackage(TdsEnums.MT_LOGIN7); writer.WriteInt32(length); if (recoverySessionData == null) { writer.WriteInt32((TdsEnums.DENALI_MAJOR << 24) | (TdsEnums.DENALI_INCREMENT << 16) | TdsEnums.DENALI_MINOR); } else { writer.WriteUInt32(recoverySessionData.TdsVersion); } writer.WriteInt32(rec.PacketSize); writer.WriteInt32(TdsEnums.CLIENT_PROG_VER); writer.WriteInt32(GetCurrentProcessIdForTdsLoginOnly()); writer.WriteInt32(0); // connectionID is unused // Log7Flags (DWORD) var 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; } writer.WriteInt32(log7Flags); writer.WriteInt32(0); // ClientTimeZone is not used writer.WriteInt32(0); // LCID is unused by server // Start writing offset and length of variable length portions var 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 writer.WriteInt16(offset); // host name offset writer.WriteInt16(rec.HostName.Length); 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) { // case where user/password data is not used, send over zeros writer.WriteInt16(0); // userName offset writer.WriteInt16(0); writer.WriteInt16(0); // password offset writer.WriteInt16(0); } else { writer.WriteInt16(offset); // userName offset writer.WriteInt16(userName.Length); offset += userName.Length * 2; // the encrypted password is a byte array - so length computations different than strings writer.WriteInt16(offset); // password offset writer.WriteInt16(encryptedPassword.Length / 2); offset += encryptedPassword.Length; } writer.WriteInt16(offset); // app name offset writer.WriteInt16(rec.ApplicationName.Length); offset += rec.ApplicationName.Length * 2; writer.WriteInt16(offset); // server name offset writer.WriteInt16(rec.ServerName.Length); offset += rec.ServerName.Length * 2; writer.WriteInt16(offset); if (useFeatureExt) { writer.WriteInt16(4); // length of ibFeatgureExtLong (which is a DWORD) offset += 4; } else { writer.WriteInt16(0); // unused (was remote password ?) } writer.WriteInt16(offset); // client interface name offset writer.WriteInt16(clientInterfaceName.Length); offset += clientInterfaceName.Length * 2; writer.WriteInt16(offset); // language name offset writer.WriteInt16(rec.Language.Length); offset += rec.Language.Length * 2; writer.WriteInt16(offset); // database name offset writer.WriteInt16(rec.Database.Length); offset += rec.Database.Length * 2; var nicAddress = GetNetworkPhysicalAddressForTdsLoginOnly(); writer.WriteByteArray(nicAddress); writer.WriteInt16(offset); // ibSSPI offset if (rec.UseSspi) { writer.WriteInt16(rec.ClientToken.Length); offset += rec.ClientToken.Length; } else { writer.WriteInt16(0); } writer.WriteInt16(offset); // DB filename offset writer.WriteInt16(rec.AttachDbFilename.Length); offset += rec.AttachDbFilename.Length * 2; writer.WriteInt16(offset); // reset password offset writer.WriteInt16(0); writer.WriteInt32(0); // reserved for chSSPI // write variable length portion writer.WriteUnicodeString(rec.HostName); // if we are using SSPI, do not send over username/password, since we will use SSPI instead // same behavior as Luxor if (!rec.UseSspi) { writer.WriteUnicodeString(userName); writer.WriteByteArray(encryptedPassword); } writer.WriteUnicodeString(rec.ApplicationName); writer.WriteUnicodeString(rec.ServerName); // write ibFeatureExtLong if (useFeatureExt) { writer.WriteInt32(feOffset); } writer.WriteUnicodeString(clientInterfaceName); writer.WriteUnicodeString(rec.Language); writer.WriteUnicodeString(rec.Database); // send over SSPI data if we are using SSPI if (rec.UseSspi) { writer.WriteByteArray(rec.ClientToken); } writer.WriteUnicodeString(rec.AttachDbFilename); if (useFeatureExt) { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { WriteSessionRecoveryFeatureRequest(writer, recoverySessionData); } if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) { WriteGlobalTransactionsFeatureRequest(writer); } writer.WriteByte(0xFF); // terminator } }
public static void SendSspi(this TdsPackageWriter writer, byte[] sspiData) { writer.NewPackage(TdsEnums.MT_SSPI); writer.WriteByteArray(sspiData); writer.SendLastMessage(); }