Esempio n. 1
0
        public static void SendRpc(this TdsPackageWriter writer, SqlCollations defaultCollation, FormattableString sql, long sqlConnectionId)
        {
            writer.NewPackage(TdsEnums.MT_RPC);
            writer.WriteMarsHeader(sqlConnectionId);

            writer.WriteInt16(0xffff);
            writer.WriteInt16(TdsEnums.RPC_PROCID_EXECUTESQL);

            // Options
            writer.WriteInt16(TdsEnums.RPC_PARAM_DEFAULT);

            // Stream out parameters
            var parameters = CreateParameters(sql);

            foreach (var parameter in parameters)
            {
                // parameters can be unnamed
                var param  = parameter;
                var value  = parameter.Value;
                var isNull = value == null;

                writer.WriteByteLenString(param.Name);

                // Write parameter status
                writer.WriteByte(parameter.Status);
                var p  = parameter;
                var mt = p.MetaData;
                WriteTdsTypeInfo(writer, mt, p.Size, p.IsNull, defaultCollation, p.Scale);
                WriteValue(writer, value, mt, isNull);
            }

            writer.SendLastMessage();
        }
Esempio n. 2
0
        private static void WriteTransactionHeader(TdsPackageWriter writer, TdsEnums.TransactionManagerRequestType request, long sqlTransactionId)
        {
            writer.NewPackage(TdsEnums.MT_TRANS);
            writer.WriteMarsHeader(sqlTransactionId);

            writer.WriteInt16((short)request); // write TransactionManager Request type
        }
Esempio n. 3
0
 public static void WriteParameterLen(this TdsPackageWriter writer, TdsMetaType.MetaDataWrite metaType, int size, bool isNull)
 {
     if (isNull)
     {
         return;
     }
     //write length of the parameter
     if (metaType.IsPlp)
     {
         writer.WriteInt64(size);
     }
     else if (metaType.IsLong)
     {
         // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
         writer.WriteInt32(size);
     }
     else if (metaType.NullableType == TdsEnums.SQLDATETIME2 || metaType.NullableType == TdsEnums.SQLTIME || metaType.NullableType == TdsEnums.SQLDATETIMEOFFSET)
     {
         writer.WriteByte((byte)size);
     }
     else if (metaType.NullableType == TdsEnums.SQLBIGVARBINARY || metaType.NullableType == TdsEnums.SQLBIGCHAR || metaType.NullableType == TdsEnums.SQLNCHAR || metaType.NullableType == TdsEnums.SQLNVARCHAR) //skip varchar
     {
         writer.WriteInt16(size);
     }
     else
     {
         writer.WriteByte((byte)size); // 1 byte for everything else
     }
 }
Esempio n. 4
0
        private static void WriteColumn(TdsPackageWriter writer, MetadataBulkCopy md)
        {
            writer.WriteInt32(0x0);

            writer.WriteByte(md.Flag1);
            writer.WriteByte(md.Flag2);
            writer.WriteByte(md.TdsType);
            var mt = TdsMetaType.TdsTypes[md.TdsType];

            if (mt.LenBytes == 1)
            {
                writer.WriteByte(md.Length);
            }
            if (mt.LenBytes == 2)
            {
                writer.WriteInt16(md.Length);
            }
            if (mt.LenBytes == 4)
            {
                writer.WriteInt32(md.Length);
            }

            if (mt.HasPrecision)
            {
                writer.WriteByte(md.Precision);
            }
            if (mt.HasScale)
            {
                writer.WriteByte(md.Scale);
            }
            if (mt.HasCollation)
            {
                writer.WriteUInt32(md.Collation.Info);
                writer.WriteByte(md.Collation.SortId);
            }

            if (mt.IsTextOrImage)
            {
                writer.WriteInt16(md.PartTableName.TableName.Length);
                writer.WriteUnicodeString(md.PartTableName.TableName);
            }

            writer.WriteByte((byte)md.Column.Length);
            writer.WriteUnicodeString(md.Column);
        }
Esempio n. 5
0
 public static void WriteTypeInfoLen(this TdsPackageWriter writer, TdsMetaType.MetaDataWrite metaType, int size, bool isNull)
 {
     //write length or isNull information about the type
     if (metaType.IsPlp)
     {
         if (isNull)
         {
             writer.WriteInt64(unchecked ((long)TdsEnums.SQL_PLP_NULL));
         }
         else
         {
             writer.WriteInt64(size);
         }
     }
     else if (metaType.IsLong)
     {
         // text/image/SQLVariant have a 4 byte length, plp datatypes have 8 byte lengths
         if (isNull)
         {
             writer.WriteInt32(unchecked ((int)TdsEnums.VARLONGNULL));
         }
         else
         {
             writer.WriteInt32(size);
         }
     }
     else if (metaType.NullableType == TdsEnums.SQLDATETIME2 || metaType.NullableType == TdsEnums.SQLTIME || metaType.NullableType == TdsEnums.SQLDATETIMEOFFSET)
     {
         if (isNull)
         {
             writer.WriteByte(TdsEnums.FIXEDNULL);
         }
         else
         {
             writer.WriteByte((byte)size);
         }
     }
     else if (metaType.NullableType == TdsEnums.SQLBIGVARBINARY || metaType.NullableType == TdsEnums.SQLBIGCHAR || metaType.NullableType == TdsEnums.SQLNCHAR || metaType.NullableType == TdsEnums.SQLNVARCHAR) //skip varchar
     {
         // non-long but variable length column, must be a BIG* type: 2 byte length
         writer.WriteInt16(isNull ? TdsEnums.VARNULL : size);
     }
     else
     {
         if (isNull)
         {
             writer.WriteByte(TdsEnums.FIXEDNULL);
         }
         else
         {
             writer.WriteByte((byte)size); // 1 byte for everything else
         }
     }
 }
Esempio n. 6
0
        public static void WriteValue(TdsPackageWriter writer, object?value, TdsMetaType.MetaDataWrite metaData, bool isNull)
        {
            // write the value now
            if (isNull)
            {
                return;
            }
            switch (value)
            {
            case string v:
                writer.WriteUnicodeString(v);
                break;

            case decimal v:
                writer.WriteSqlDecimal(v, 17);
                break;

            case bool v:
                writer.WriteByte(v ? 1 : 0);
                break;

            case DateTime v:
                writer.WriteDateTime(v);
                break;

            case byte v:
                writer.WriteByte(v);
                break;

            case short v:
                writer.WriteInt16(v);
                break;

            case int v:
                writer.WriteInt32(v);
                break;

            case long v:
                writer.WriteInt64(v);
                break;

            case float v:
                writer.WriteFloat(v);
                break;

            case double v:
                writer.WriteDouble(v);
                break;
            }
        }
Esempio n. 7
0
        public static void WriteMarsHeader(this TdsPackageWriter writer, long sqlTransactionId)
        {
            const int marsHeaderSize    = 18; // 4 + (2 + 8 + 4)= size + data
            const int totalHeaderLength = 22; // 4 + (4 + 2 + 8 + 4) size+mars

            writer.WriteInt32(totalHeaderLength);

            writer.WriteInt32(marsHeaderSize);

            writer.WriteInt16(TdsEnums.HEADERTYPE_MARS);

            writer.WriteInt64(sqlTransactionId);

            writer.WriteInt32(1);
        }
Esempio n. 8
0
        public static void WriteBulkInsertColMetaData(TdsPackageWriter writer)
        {
            //0x81,0x01,0x00,//colmetadata 0x001 column
            //0x00,0x00,0x00,0x00,//usertype ignore
            //0x09,0x00, //flags
            //0x26,0x04, //int 4
            //0x02,0x49,0x00,0x64,0x00, //columnname 2 chars ID
            var columns = writer.ColumnsMetadata;

            writer.WriteByte(TdsEnums.SQLCOLMETADATA);
            writer.WriteInt16(columns.Length);
            foreach (var column in columns)
            {
                WriteColumn(writer, column);
            }
        }
Esempio n. 9
0
 public void WriteSqlInt16(short value, int index)
 {
     _writer.WriteInt16(value);
 }
Esempio n. 10
0
        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
            }
        }