From 7d884df97c5e9c6bad6b2eaf0ff2b81b154a5371 Mon Sep 17 00:00:00 2001 From: JPaja <44926986+JPaja@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:02:25 +0200 Subject: [PATCH 1/7] Expose ReadOnly Intent in TDSSQLTestClient --- TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index 6f46112..6fae755 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -40,7 +40,7 @@ public class TDSSQLTestClient /// User password /// Database to connect to /// Encryption Protocol - public TDSSQLTestClient(string server, int port, string userID, string password, string database, SslProtocols encryptionProtocol = SslProtocols.Tls12) + public TDSSQLTestClient(string server, int port, string userID, string password, string database, SslProtocols encryptionProtocol = SslProtocols.Tls12, TDSLogin7TypeFlagsReadOnlyIntent readOnlyIntent = TDSLogin7TypeFlagsReadOnlyIntent.Off) { if (string.IsNullOrEmpty(server) || string.IsNullOrEmpty(userID) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(database)) { @@ -55,6 +55,7 @@ public TDSSQLTestClient(string server, int port, string userID, string password, this.Password = password; this.Database = database; this.EncryptionProtocol = encryptionProtocol; + this.ReadOnlyIntent = readOnlyIntent; this.connectionAttempt = 0; LoggingUtilities.WriteLog($" Instantiating TDSSQLTestClient with the following parameters:"); @@ -110,6 +111,11 @@ public TDSSQLTestClient(string server, int port, string userID, string password, /// public SslProtocols EncryptionProtocol { get; set; } + /// + /// Gets or sets the ReadOnly Intent Policy. + /// + public TDSLogin7TypeFlagsReadOnlyIntent ReadOnlyIntent {get; set;} + /// /// Sends PreLogin message to the server. /// @@ -163,7 +169,7 @@ public void SendLogin7() tdsMessageBody.TypeFlags.OLEDB = TDSLogin7TypeFlagsOLEDB.On; tdsMessageBody.TypeFlags.SQLType = TDSLogin7TypeFlagsSQLType.DFLT; - tdsMessageBody.TypeFlags.ReadOnlyIntent = TDSLogin7TypeFlagsReadOnlyIntent.On; + tdsMessageBody.TypeFlags.ReadOnlyIntent = this.ReadOnlyIntent; this.TdsCommunicator.SendTDSMessage(tdsMessageBody); From c1a36709ea4fe09369f257df0de6c6361e63376f Mon Sep 17 00:00:00 2001 From: JPaja Date: Tue, 19 Sep 2023 14:06:07 +0200 Subject: [PATCH 2/7] Add: Base query support Fix: Program Fix: Program --- .../TDSClient/TDS/Client/TDSSQLTestClient.cs | 126 ++++++++++++------ .../TDSClient/TDS/Comms/TDSCommunicator.cs | 25 +++- .../TDS/Comms/TDSCommunicatorState.cs | 7 +- .../TDS/Tranasction/Header/HeaderType.cs | 13 ++ .../Headers/QueryNotificationsHeaderData.cs | 38 ++++++ .../Header/Headers/TraceActivityHeaderData.cs | 26 ++++ .../TransactionDescriptorHeaderData.cs | 42 ++++++ .../TDS/Tranasction/Header/ISqlHeaderData.cs | 14 ++ .../Header/SqlAllHeadersPacketData.cs | 30 +++++ .../Tranasction/Header/SqlHeaderPacketData.cs | 60 +++++++++ .../TDS/Tranasction/TDSSqlBatchPacketData.cs | 49 +++++++ .../TDS/Utilities/BigEndianUtilities.cs | 33 +++++ TDSClient/TDSClientLiveTest/Program.cs | 9 +- 13 files changed, 425 insertions(+), 47 deletions(-) create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs create mode 100644 TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index 6f46112..7e3c23d 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -7,16 +7,21 @@ namespace TDSClient.TDS.Client { using System; + using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Sockets; using System.Security.Authentication; + using System.Threading; using TDSClient.TDS.Comms; using TDSClient.TDS.Header; using TDSClient.TDS.Login7; using TDSClient.TDS.PreLogin; + using TDSClient.TDS.Query; using TDSClient.TDS.Tokens; + using TDSClient.TDS.Tranasction.Header.Headers; + using TDSClient.TDS.Tranasction.Headers; using TDSClient.TDS.Utilities; /// @@ -212,46 +217,7 @@ public void ReceiveLogin7Response() { foreach (var token in response.Tokens) { - if (token is TDSEnvChangeToken) - { - var envChangeToken = token as TDSEnvChangeToken; - if (envChangeToken.Type == Tokens.EnvChange.TDSEnvChangeType.Routing) - { - LoggingUtilities.WriteLog($" Client received EnvChange routing token, client is being routed."); - this.Server = envChangeToken.Values["AlternateServer"]; - this.Port = int.Parse(envChangeToken.Values["ProtocolProperty"]); - this.reconnect = true; - LoggingUtilities.WriteLog($" Redirect to {this.Server}:{this.Port}", writeToSummaryLog: true, writeToVerboseLog: false); - } - } - else if (token is TDSErrorToken) - { - var errorToken = token as TDSErrorToken; - LoggingUtilities.WriteLog($" Client received Error token, Number: {errorToken.Number}, State: {errorToken.State}", writeToSummaryLog: true); ; - LoggingUtilities.WriteLog($" MsgText: {errorToken.MsgText}"); - LoggingUtilities.WriteLog($" Class: {errorToken.Class}"); - LoggingUtilities.WriteLog($" ServerName: {errorToken.ServerName}"); - LoggingUtilities.WriteLog($" ProcName: {errorToken.ProcName}"); - LoggingUtilities.WriteLog($" LineNumber: {errorToken.LineNumber}"); - - if (errorToken.Number == 18456) - { - throw new Exception("Login failure."); - } - } - else if (token is TDSInfoToken) - { - var infoToken = token as TDSInfoToken; - LoggingUtilities.WriteLog($" Client received Info token:"); - - LoggingUtilities.WriteLog($" Number: {infoToken.Number}"); - LoggingUtilities.WriteLog($" State: {infoToken.State}"); - LoggingUtilities.WriteLog($" Class: {infoToken.Class}"); - LoggingUtilities.WriteLog($" MsgText: {infoToken.MsgText}"); - LoggingUtilities.WriteLog($" ServerName: {infoToken.ServerName}"); - LoggingUtilities.WriteLog($" ProcName: {infoToken.ProcName}"); - LoggingUtilities.WriteLog($" LineNumber: {infoToken.LineNumber}"); - } + PrintTdsToken(token); } } else @@ -262,6 +228,50 @@ public void ReceiveLogin7Response() LoggingUtilities.WriteLog($" Login7 response received."); } + private void PrintTdsToken(TDSToken token) + { + if (token is TDSEnvChangeToken) + { + var envChangeToken = token as TDSEnvChangeToken; + if (envChangeToken.Type == Tokens.EnvChange.TDSEnvChangeType.Routing) + { + LoggingUtilities.WriteLog($" Client received EnvChange routing token, client is being routed."); + this.Server = envChangeToken.Values["AlternateServer"]; + this.Port = int.Parse(envChangeToken.Values["ProtocolProperty"]); + this.reconnect = true; + LoggingUtilities.WriteLog($" Redirect to {this.Server}:{this.Port}", writeToSummaryLog: true, writeToVerboseLog: false); + } + } + else if (token is TDSErrorToken) + { + var errorToken = token as TDSErrorToken; + LoggingUtilities.WriteLog($" Client received Error token, Number: {errorToken.Number}, State: {errorToken.State}", writeToSummaryLog: true); ; + LoggingUtilities.WriteLog($" MsgText: {errorToken.MsgText}"); + LoggingUtilities.WriteLog($" Class: {errorToken.Class}"); + LoggingUtilities.WriteLog($" ServerName: {errorToken.ServerName}"); + LoggingUtilities.WriteLog($" ProcName: {errorToken.ProcName}"); + LoggingUtilities.WriteLog($" LineNumber: {errorToken.LineNumber}"); + + if (errorToken.Number == 18456) + { + throw new Exception("Login failure."); + } + } + else if (token is TDSInfoToken) + { + var infoToken = token as TDSInfoToken; + LoggingUtilities.WriteLog($" Client received Info token:"); + + LoggingUtilities.WriteLog($" Number: {infoToken.Number}"); + LoggingUtilities.WriteLog($" State: {infoToken.State}"); + LoggingUtilities.WriteLog($" Class: {infoToken.Class}"); + LoggingUtilities.WriteLog($" MsgText: {infoToken.MsgText}"); + LoggingUtilities.WriteLog($" ServerName: {infoToken.ServerName}"); + LoggingUtilities.WriteLog($" ProcName: {infoToken.ProcName}"); + LoggingUtilities.WriteLog($" LineNumber: {infoToken.LineNumber}"); + } + } + /// /// Connect to the server. /// @@ -332,6 +342,42 @@ public void Connect() } } + public int queryCount = 0; + public Dictionary Query(string query) + { + var id = Interlocked.Increment(ref queryCount); + try + { + var request = new TDSSqlBatchPacketData(query); + var header = new SqlHeaderPacketData(new TransactionDescriptorHeaderData((uint)id)); + request.AllHeaders.Headers.Add(header); + + TdsCommunicator.SendTDSMessage(request); + + if (this.TdsCommunicator.ReceiveTDSMessage() is TDSTokenStreamPacketData response) + { + foreach (var token in response.Tokens) + { + PrintTdsToken(token); + } + } + else + { + throw new InvalidOperationException(); + } + } + catch(Exception ex) + { + LoggingUtilities.WriteLog($"Exception:"); + LoggingUtilities.WriteLog($"{ex.Message}"); + if (ex.InnerException != null) + { + LoggingUtilities.WriteLog($"InnerException: {ex.InnerException.Message}"); + } + } + return null; + } + private void MeasureDNSResolutionTime() { try diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs index 5fcbb2e..97a6f93 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs @@ -17,6 +17,7 @@ namespace TDSClient.TDS.Comms using TDSClient.TDS.Interfaces; using TDSClient.TDS.Login7; using TDSClient.TDS.PreLogin; + using TDSClient.TDS.Query; using TDSClient.TDS.Tokens; using TDSClient.TDS.Utilities; using static System.Net.Mime.MediaTypeNames; @@ -227,6 +228,16 @@ public ITDSPacketData ReceiveTDSMessage() break; } + case TDSCommunicatorState.SentSqlBatch: + { + result = new TDSTokenStreamPacketData(); + result.Unpack(new MemoryStream(resultBuffer)); + + //Restore to base state + this.communicatorState = TDSCommunicatorState.LoggedIn; + break; + } + default: { throw new InvalidOperationException(); @@ -268,9 +279,14 @@ public void SendTDSMessage(ITDSPacketData data) case TDSCommunicatorState.LoggedIn: { - throw new NotSupportedException(); - } + if (!(data is TDSSqlBatchPacketData)) + { + throw new InvalidDataException(); + } + this.innerTdsStream.CurrentOutboundMessageType = TDSMessageType.SQLBatch; + break; + } default: { throw new InvalidOperationException(); @@ -295,6 +311,11 @@ public void SendTDSMessage(ITDSPacketData data) this.communicatorState = TDSCommunicatorState.SentLogin7RecordWithCompleteAuthToken; break; } + case TDSCommunicatorState.LoggedIn: + { + this.communicatorState = TDSCommunicatorState.SentSqlBatch; + break; + } } } } diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs index 44cb7bd..bb0cf60 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs @@ -29,6 +29,11 @@ public enum TDSCommunicatorState /// /// LoggedIn Communicator State /// - LoggedIn + LoggedIn, + + /// + /// Sent sql batch + /// + SentSqlBatch, } } diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs new file mode 100644 index 0000000..321aa7f --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/HeaderType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TDSClient.TDS.Tranasction.Header +{ + public enum HeaderType : ushort + { + QueryNotifications = 0x1, + TransactionDescriptor = 0x2, + TraceActivity = 0x3, + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs new file mode 100644 index 0000000..6355d60 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs @@ -0,0 +1,38 @@ +using System.IO; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class QueryNotificationsHeaderData : ISqlHeaderData + { + public HeaderType HeaderType => HeaderType.QueryNotifications; + public uint Size => sizeof(ushort) + (uint)NotifyId.Length * 2 + sizeof(ushort) + (uint)SSBDeployment.Length * 2 + sizeof(ulong); + public string NotifyId { get; set; } + public string SSBDeployment { get; set; } + public ulong NotifyTimeout { get; set; } + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUShort(stream, (ushort)NotifyId.Length); + BigEndianUtilities.WriteUnicodeStream(stream, NotifyId); + BigEndianUtilities.WriteUShort(stream, (ushort)SSBDeployment.Length); + BigEndianUtilities.WriteUnicodeStream(stream, SSBDeployment); + BigEndianUtilities.WriteULong(stream, NotifyTimeout); + } + + public bool Unpack(MemoryStream stream) + { + var notifyIdLen = BigEndianUtilities.ReadUShort(stream); + var notifyIdChars = BigEndianUtilities.ReadUnicodeStream(stream, notifyIdLen); + NotifyId = new string(notifyIdChars); + + var ssbDeploymentLen = BigEndianUtilities.ReadUShort(stream); + var ssbDeploymentChars = BigEndianUtilities.ReadUnicodeStream(stream, ssbDeploymentLen); + SSBDeployment = new string(ssbDeploymentChars); + + NotifyTimeout = BigEndianUtilities.ReadULong(stream); + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs new file mode 100644 index 0000000..c21d6f1 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TraceActivityHeaderData.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Tranasction.Headers; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class TraceActivityHeaderData : ISqlHeaderData + { + public HeaderType HeaderType => HeaderType.TraceActivity; + + public Guid ActivityId { get; set; } + public uint Size => 16; + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs new file mode 100644 index 0000000..d415c73 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Header.Headers +{ + public class TransactionDescriptorHeaderData : ISqlHeaderData + { + internal TransactionDescriptorHeaderData() + { + + } + + public TransactionDescriptorHeaderData(uint id) + { + TransactionDescriptor = id; + OutstandingRequestCount = 0; + } + + public ulong TransactionDescriptor { get; set; } + public uint OutstandingRequestCount { get; set; } = 0; + public uint Size => sizeof(ulong) + sizeof(uint); + public HeaderType HeaderType => HeaderType.TransactionDescriptor; + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteULong(stream, TransactionDescriptor); + BigEndianUtilities.WriteUInt(stream, OutstandingRequestCount); + } + + public bool Unpack(MemoryStream stream) + { + TransactionDescriptor = BigEndianUtilities.ReadULong(stream); + OutstandingRequestCount = BigEndianUtilities.ReadUInt(stream); + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs new file mode 100644 index 0000000..171acd4 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/ISqlHeaderData.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Header; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public interface ISqlHeaderData : IPackageable + { + HeaderType HeaderType { get; } + uint Size { get; } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs new file mode 100644 index 0000000..db08bd4 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public class SqlAllHeadersPacketData : IPackageable + { + public List Headers { get; set; } = new List(); + public uint TotalLength => sizeof(uint) + (uint)Headers.Sum(x => x.Size); + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUInt(stream, TotalLength); + foreach (var header in Headers) + { + header.Pack(stream); + } + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs new file mode 100644 index 0000000..8762c27 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs @@ -0,0 +1,60 @@ +using System.IO; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tranasction.Header; +using TDSClient.TDS.Tranasction.Header.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tranasction.Headers +{ + public class SqlHeaderPacketData : IPackageable + { + internal SqlHeaderPacketData() + { + + } + + public SqlHeaderPacketData(ISqlHeaderData data) + { + Data = data; + } + + public ISqlHeaderData Data { get; set; } + + public uint Size => Data.Size + sizeof(ushort) + sizeof(uint); + + public void Pack(MemoryStream stream) + { + BigEndianUtilities.WriteUInt(stream, Size); + BigEndianUtilities.WriteUShort(stream, (ushort)Data.HeaderType); + Data.Pack(stream); + } + + public bool Unpack(MemoryStream stream) + { + var headerLength = BigEndianUtilities.ReadUInt(stream); + var headerType = (HeaderType)BigEndianUtilities.ReadUShort(stream); + ISqlHeaderData data; + switch(headerType) + { + case HeaderType.QueryNotifications: + data = new QueryNotificationsHeaderData(); + break; + case HeaderType.TransactionDescriptor: + data = new TransactionDescriptorHeaderData(); + break; + case HeaderType.TraceActivity: + data = new TraceActivityHeaderData(); + break; + default: + stream.Seek(headerLength - (sizeof(ushort) + sizeof(uint)), SeekOrigin.Current); + return false; + } + if (!data.Unpack(stream)) + { + return false; + } + Data = data; + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs new file mode 100644 index 0000000..3edbc9a --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using TDSClient.TDS.Client; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.PreLogin; +using TDSClient.TDS.Tranasction.Headers; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Query +{ +#pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() + public class TDSSqlBatchPacketData : ITDSPacketData, IEquatable +#pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() + { + + private string _query = string.Empty; + + public TDSSqlBatchPacketData(string query) + { + _query = query; + } + + public SqlAllHeadersPacketData AllHeaders { get; set; } = new SqlAllHeadersPacketData(); + + public string SqlText => _query; + + public bool Equals(TDSSqlBatchPacketData other) + { + throw new NotImplementedException(); + } + + public ushort Length() => (ushort)(AllHeaders.TotalLength + SqlText.Length * sizeof(char)); + + public void Pack(MemoryStream stream) + { + AllHeaders.Pack(stream); + BigEndianUtilities.WriteUnicodeStream(stream, SqlText); + } + + public bool Unpack(MemoryStream stream) + { + //TODO: Get original length from tds packet size + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs index 0b29b13..017aba4 100644 --- a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs +++ b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs @@ -7,6 +7,7 @@ namespace TDSClient.TDS.Utilities { using System; + using System.Collections.Generic; using System.IO; /// @@ -68,6 +69,20 @@ public static void WriteByteArray(MemoryStream stream, byte[] array) } } + /// + /// Used to write string to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Unicode chars to write to MemoryStream. + public static void WriteUnicodeStream(MemoryStream stream, IEnumerable chars) + { + foreach (char c in chars) + { + WriteUShort(stream, c); + } + } + + /// /// Used to read a UShort value from stream in big endian order. /// @@ -135,5 +150,23 @@ public static byte[] ReadByteArray(MemoryStream stream, uint length) return result; } + + /// + /// Used to read a Unicode char array from stream in big endian order. + /// + /// MemoryStream from which to read the unicode char array. + /// Length of the unicode array to read. + /// Unicode char array read from the stream. + public static char[] ReadUnicodeStream(MemoryStream stream, int length) + { + char[] result = new char[length]; + //TODO: Check order to read in + for (int i = 1; i <= length; i++) + { + result[length - i] = (char)ReadUShort(stream); + } + + return result; + } } } diff --git a/TDSClient/TDSClientLiveTest/Program.cs b/TDSClient/TDSClientLiveTest/Program.cs index 611020d..8bb55bd 100644 --- a/TDSClient/TDSClientLiveTest/Program.cs +++ b/TDSClient/TDSClientLiveTest/Program.cs @@ -1,4 +1,6 @@ -using System; +using System; +using System.Security.Authentication; +using System.Text; using System.Threading; using TDSClient.TDS.Client; @@ -12,16 +14,15 @@ class Program public static string Password = ""; public static string Database = ""; - static void Main(string[] args) { - TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database); + TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database, SslProtocols.None); TDSClient.TDS.Utilities.LoggingUtilities.SetVerboseLog(Console.Out); - //TDSClient.TDS.Utilities.LoggingUtilities.SetSummaryLog(Console.Out); for (int i = 0; i < 1; i++) { tdsClient.Connect(); + tdsClient.Query("select 1"); tdsClient.Disconnect(); Console.WriteLine(); Thread.Sleep(1000); From e5673996a8a3011660a101627ecd374e43447023 Mon Sep 17 00:00:00 2001 From: JPaja Date: Thu, 14 Sep 2023 23:41:05 +0200 Subject: [PATCH 3/7] Add types base --- .../TDSClient/TDS/Tokens/Cols/ColumnData.cs | 27 +++++ .../TDS/Tokens/Cols/TDSAltRowToken.cs | 31 +++++ .../TDS/Tokens/Cols/TDSColMetadataToken.cs | 34 ++++++ .../TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs | 31 +++++ .../TDSClient/TDS/Tokens/Cols/TDSRowToken.cs | 31 +++++ .../TDSClient/TDS/Tokens/TDSTokenFactory.cs | 32 +++++ .../TDSClient/TDS/Tokens/Type/TVTypeInfo.cs | 24 ++++ .../TDSClient/TDS/Tokens/Type/TypeInfo.cs | 22 ++++ .../TDSClient/TDS/Tokens/Type/TypeVarLen.cs | 10 ++ .../TDSClient/TDS/Tokens/Type/VarLenType.cs | 114 ++++++++++++++++++ 10 files changed, 356 insertions(+) create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs new file mode 100644 index 0000000..0e84686 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/ColumnData.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + public class ColumnData : IPackageable + { + internal ColumnData() { } + + public ulong UserType { get; set; } + public ushort Flags { get; set; } + + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs new file mode 100644 index 0000000..8bb4e3a --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSAltRowToken.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSAltRowToken : TDSToken + { + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs new file mode 100644 index 0000000..f0ac27d --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSColMetadataToken : TDSToken + { + + public ushort Count { get; set; } + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs new file mode 100644 index 0000000..ba28e6f --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSDoneToken : TDSToken + { + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs new file mode 100644 index 0000000..91f7cc5 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSRowToken : TDSToken + { + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs index dc0e2b9..3cbba0a 100644 --- a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs +++ b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs @@ -8,6 +8,7 @@ namespace TDSClient.TDS.Tokens { using System; using System.IO; + using TDSClient.TDS.Tokens.Cols; using TDSClient.TDS.Utilities; /// @@ -48,6 +49,37 @@ public static TDSToken ReadTokenFromStream(MemoryStream stream) var token = new TDSInfoToken(); token.Unpack(stream); + return token; + } + case TDSTokenType.ColMetadata: + { + var token = new TDSColMetadataToken(); + token.Unpack(stream); + + return token; + } + + case TDSTokenType.AltRow: + { + var token = new TDSAltRowToken(); + token.Unpack(stream); + + return token; + } + + case TDSTokenType.Row: + { + var token = new TDSRowToken(); + token.Unpack(stream); + + return token; + } + + case TDSTokenType.Done: + { + var token = new TDSRowToken(); + token.Unpack(stream); + return token; } diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs new file mode 100644 index 0000000..1d0c6e0 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Type +{ + public class TVTypeInfo : IPackageable + { + + public VarLenType VarLenType { get; set; } + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs new file mode 100644 index 0000000..98bd312 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Type +{ + public class TypeInfo : IPackageable + { + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + throw new NotImplementedException(); + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs new file mode 100644 index 0000000..e2a79b5 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TDSClient.TDS.Tokens.Type +{ + internal class TypeVarLen + { + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs b/TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs new file mode 100644 index 0000000..24edc92 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TDSClient.TDS.Tokens.Type +{ + [Flags] + public enum VarLenType: byte + { + //Zero length data types + NULLTYPE = 0x1F, + + //Fixed lenght data types + INT1TYPE = 0x30, + BITTYPE = 0x32, + INT2TYPE = 0x34, + INT4TYPE = 0x38, + DATETIM4TYPE = 0x3A, + FLT4TYPE = 0x3B, + MONEYTYPE = 0x3C, + DATETIMETYPE = 0x3D, + FLT8TYPE = 0x3E, + MONEY4TYPE = 0x7A, + INT8TYPE = 0x7F, + DECIMALTYPE = 0x37, + NUMERICTYPE = 0x3F, + + FIXEDLENTYPE = INT1TYPE + | BITTYPE + | INT2TYPE + | INT4TYPE + | DATETIM4TYPE + | FLT4TYPE + | MONEYTYPE + | DATETIMETYPE + | FLT8TYPE + | MONEY4TYPE + | INT8TYPE, + + //Variable length data types + GUIDTYPE = 0x24, + INTNTYPE = 0x26, + BITNTYPE = 0x68, + DECIMALNTYPE = 0x6A, + NUMERICNTYPE = 0x6C, + FLTNTYPE = 0x6D, + MONEYNTYPE = 0x6E, + DATETIMNTYPE = 0x6F, + DATENTYPE = 0x28, + TIMENTYPE = 0x29, + DATETIME2NTYPE = 0x2A, + DATETIMEOFFSETNTYPE = 0x2B, + CHARTYPE = 0x2F, + VARCHARTYPE = 0x27, + BINARYTYPE = 0x2D, + VARBINARYTYPE = 0x25, + BIGVARBINARYTYPE = 0xA5, + BIGVARCHARTYPE = 0xA7, + BIGBINARYTYPE = 0xAD, + BIGCHARTYPE = 0xAF, + NVARCHARTYPE = 0xE7, + NCHARTYPE = 0xEF, + XMLTYPE = 0xF1, + UDTTYPE = 0xF0, + TEXTTYPE = 0x23, + IMAGETYPE = 0x22, + NTEXTTYPE = 0x63, + SSVARIANTTYPE = 0x62, + + BYTELEN_TYPE = GUIDTYPE + | INTNTYPE + | DECIMALTYPE + | NUMERICTYPE + | BITNTYPE + | DECIMALNTYPE + | NUMERICNTYPE + | FLTNTYPE + | MONEYNTYPE + | DATETIMNTYPE + | DATENTYPE + | TIMENTYPE + | DATETIME2NTYPE + | DATETIMEOFFSETNTYPE + | CHARTYPE + | VARCHARTYPE + | BINARYTYPE + | VARBINARYTYPE, + + USHORTLEN_TYPE = BIGVARBINARYTYPE + | BIGVARCHARTYPE + | BIGBINARYTYPE + | BIGCHARTYPE + | NVARCHARTYPE + | NCHARTYPE, + + LONGLEN_TYPE = IMAGETYPE + | NTEXTTYPE + | SSVARIANTTYPE + | TEXTTYPE + | XMLTYPE, + + VARLENTYPE = BYTELEN_TYPE + | USHORTLEN_TYPE + | LONGLEN_TYPE, + + //Partially length type + PARTLENTYPE = XMLTYPE + | BIGVARCHARTYPE + | BIGVARBINARYTYPE + | NVARCHARTYPE + | UDTTYPE, + + } +} From aefae973caa6d171f752091a5d28df38fc9758f3 Mon Sep 17 00:00:00 2001 From: JPaja Date: Tue, 19 Sep 2023 14:01:20 +0200 Subject: [PATCH 4/7] Add basic query support --- .../TDSClient/TDS/Comms/TDSCommunicator.cs | 15 ++- .../TDS/Comms/TDSCommunicatorState.cs | 5 + .../TDSClient/TDS/Tokens/Cols/ColMetadata.cs | 61 +++++++++ .../TDS/Tokens/Cols/TDSColMetadataToken.cs | 21 ++- .../TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs | 31 ----- .../TDSClient/TDS/Tokens/Cols/TDSRowToken.cs | 20 ++- .../TDS/Tokens/Cols/TDSSqlReturnValueToken.cs | 66 +++++++++ .../TDSClient/TDS/Tokens/TDSTokenFactory.cs | 37 +++-- .../TDS/Tokens/TDSTokenStreamPacketData.cs | 2 +- .../TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs | 59 ++++++++ .../TDS/Tokens/Type/SqlTypeValueFactory.cs | 126 ++++++++++++++++++ .../Type/{VarLenType.cs => SqlTypes.cs} | 2 +- .../TDSClient/TDS/Tokens/Type/TVTypeInfo.cs | 24 ---- .../TDSClient/TDS/Tokens/Type/TypeInfo.cs | 22 --- .../TDSClient/TDS/Tokens/Type/TypeVarLen.cs | 10 -- .../TransactionDescriptorHeaderData.cs | 8 +- .../Header/SqlAllHeadersPacketData.cs | 2 +- .../Tranasction/Header/SqlHeaderPacketData.cs | 4 +- .../TDS/Tranasction/TDSSqlBatchPacketData.cs | 4 +- .../TDS/Utilities/BigEndianUtilities.cs | 93 ++++++++++++- TDSClient/TDSClientLiveTest/Program.cs | 4 +- .../TDSClientLiveTest.csproj | 3 +- 22 files changed, 503 insertions(+), 116 deletions(-) create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs delete mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs create mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs rename TDSClient/TDSClient/TDS/Tokens/Type/{VarLenType.cs => SqlTypes.cs} (98%) delete mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs delete mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs delete mode 100644 TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs index 97a6f93..0310ed5 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicator.cs @@ -223,8 +223,17 @@ public ITDSPacketData ReceiveTDSMessage() case TDSCommunicatorState.SentLogin7RecordWithCompleteAuthToken: { - result = new TDSTokenStreamPacketData(); + var tokenStream = new TDSTokenStreamPacketData(); + result = tokenStream; result.Unpack(new MemoryStream(resultBuffer)); + if (tokenStream.Tokens.Any( t=> t is TDSErrorToken err)) + { + this.communicatorState = TDSCommunicatorState.LoginError; + } + else + { + this.communicatorState = TDSCommunicatorState.LoggedIn; + } break; } @@ -294,9 +303,11 @@ public void SendTDSMessage(ITDSPacketData data) } var buffer = new byte[data.Length()]; - data.Pack(new MemoryStream(buffer)); + var memStream = new MemoryStream(buffer); + data.Pack(memStream); this.innerStream.Write(buffer, 0, buffer.Length); + var hex = BitConverter.ToString(buffer).Replace("-", " "); switch (this.communicatorState) { diff --git a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs index bb0cf60..15146c3 100644 --- a/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs +++ b/TDSClient/TDSClient/TDS/Comms/TDSCommunicatorState.cs @@ -31,6 +31,11 @@ public enum TDSCommunicatorState /// LoggedIn, + /// + /// LoginError Communicator State + /// + LoginError, + /// /// Sent sql batch /// diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs new file mode 100644 index 0000000..26514f0 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + public class ColMetadata : TDSToken + { + public uint UserType { get; set; } + public byte Flags1 { get; set; } + public byte Flags2 { get; set; } + public SqlTypeInfo Type { get; set; } + + //#[Nullable] + public string ColumnName { get; set; } = null; + + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + //TODO: IsColumnEncryptionSupported read ushort + + UserType = BigEndianUtilities.ReadUInt(stream); + + Flags1 = Convert.ToByte(stream.ReadByte()); + Flags2 = Convert.ToByte(stream.ReadByte()); + + var type = new SqlTypeInfo(); + if (!type.Unpack(stream)) + { + return false; + } + Type = type; + + var colNameLen = stream.ReadByte(); + + var colName = BigEndianUtilities.ReadUnicodeStream(stream, colNameLen); + ColumnName = new string(colName); + + return true; + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs index f0ac27d..286787f 100644 --- a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSColMetadataToken.cs @@ -3,6 +3,8 @@ using System.IO; using System.Text; using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; namespace TDSClient.TDS.Tokens.Cols { @@ -11,6 +13,8 @@ internal class TDSColMetadataToken : TDSToken public ushort Count { get; set; } + public ColMetadata[] Metadata { get; set; } + public override bool Equals(object obj) { throw new NotImplementedException(); @@ -28,7 +32,22 @@ public override void Pack(MemoryStream stream) public override bool Unpack(MemoryStream stream) { - throw new NotImplementedException(); + Count = BigEndianUtilities.ReadUShortLE(stream); + + var metadata = new ColMetadata[Count]; + for (int i = 0; i < Count;i++) + { + var colMetadata = new ColMetadata(); + if(!colMetadata.Unpack(stream)) + { + return false; + } + + metadata[i] = colMetadata; + } + + Metadata = metadata; + return true; } } } diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs deleted file mode 100644 index ba28e6f..0000000 --- a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSDoneToken.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using TDSClient.TDS.Interfaces; - -namespace TDSClient.TDS.Tokens.Cols -{ - internal class TDSDoneToken : TDSToken - { - public override bool Equals(object obj) - { - throw new NotImplementedException(); - } - - public override ushort Length() - { - throw new NotImplementedException(); - } - - public override void Pack(MemoryStream stream) - { - throw new NotImplementedException(); - } - - public override bool Unpack(MemoryStream stream) - { - throw new NotImplementedException(); - } - } -} diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs index 91f7cc5..f5e5ce8 100644 --- a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSRowToken.cs @@ -1,13 +1,24 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices.ComTypes; using System.Text; using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; namespace TDSClient.TDS.Tokens.Cols { internal class TDSRowToken : TDSToken { + public TDSRowToken(TDSColMetadataToken colMetadata) + { + ColMetadata = colMetadata; + } + + public object[] Values { get; set; } = null; + public TDSColMetadataToken ColMetadata { get; } + public override bool Equals(object obj) { throw new NotImplementedException(); @@ -25,7 +36,14 @@ public override void Pack(MemoryStream stream) public override bool Unpack(MemoryStream stream) { - throw new NotImplementedException(); + Values = new object[ColMetadata.Count]; + for (int i = 0; i < ColMetadata.Count; i++) + { + var type = ColMetadata.Metadata[i].Type; + Values[i] = SqlTypeValueFactory.ReadVaue(stream, type); + } + + return true; } } } diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs new file mode 100644 index 0000000..16cf8b4 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using TDSClient.TDS.Interfaces; +using TDSClient.TDS.Tokens.Type; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Cols +{ + internal class TDSSqlReturnValueToken : TDSToken + { + public ushort Index { get; set; } + //[Nullable] + public string ParameterName { get; set; } = null; + public byte Status { get; set; } + public uint UserType { get; set; } + public byte Flags1 { get; set; } + public byte Flags2 { get; set; } + public SqlTypeInfo Type { get; set; } + + public object Value { get; set; } = null; + + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override ushort Length() + { + throw new NotImplementedException(); + } + + public override void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public override bool Unpack(MemoryStream stream) + { + Index = BigEndianUtilities.ReadUShortLE(stream); + var paraeterNameLen = stream.ReadByte(); + var paraeterName = BigEndianUtilities.ReadUnicodeStream(stream, paraeterNameLen); + ParameterName = new string(paraeterName); + Status = Convert.ToByte(stream.ReadByte()); + + UserType = BigEndianUtilities.ReadUInt(stream); + Flags1 = Convert.ToByte(stream.ReadByte()); + Flags2 = Convert.ToByte(stream.ReadByte()); + + var type = new SqlTypeInfo(); + if (!type.Unpack(stream)) + { + return false; + } + Type = type; + + + Value = SqlTypeValueFactory.ReadVaue(stream, type); + + return true; + } + + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs index 3cbba0a..153f144 100644 --- a/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs +++ b/TDSClient/TDSClient/TDS/Tokens/TDSTokenFactory.cs @@ -7,7 +7,9 @@ namespace TDSClient.TDS.Tokens { using System; + using System.Collections.Generic; using System.IO; + using System.Linq; using TDSClient.TDS.Tokens.Cols; using TDSClient.TDS.Utilities; @@ -21,7 +23,7 @@ public static class TDSTokenFactory /// /// Stream that contains the token /// Returns read TDS Token - public static TDSToken ReadTokenFromStream(MemoryStream stream) + public static TDSToken ReadTokenFromStream(MemoryStream stream, List oldTokens) { var tokenType = (TDSTokenType)stream.ReadByte(); @@ -58,10 +60,9 @@ public static TDSToken ReadTokenFromStream(MemoryStream stream) return token; } - - case TDSTokenType.AltRow: + case TDSTokenType.ReturnValue: { - var token = new TDSAltRowToken(); + var token = new TDSSqlReturnValueToken(); token.Unpack(stream); return token; @@ -69,20 +70,36 @@ public static TDSToken ReadTokenFromStream(MemoryStream stream) case TDSTokenType.Row: { - var token = new TDSRowToken(); - token.Unpack(stream); + if(oldTokens.SingleOrDefault(t => t is TDSColMetadataToken) is TDSColMetadataToken colMetadata) + { + var token = new TDSRowToken(colMetadata); + token.Unpack(stream); - return token; - } + return token; + } - case TDSTokenType.Done: + throw new Exception("No Column metadata found"); + } + /* + case TDSTokenType.AltRow: { - var token = new TDSRowToken(); + var token = new TDSAltRowToken(); token.Unpack(stream); return token; } + */ + + /* case TDSTokenType.Done: + { + return null; + //var token = new TDSRowToken(); + //token.Unpack(stream); + + //return token; + }*/ + default: { IgnoreToken(tokenType, stream); diff --git a/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs b/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs index 3c969e9..e1e4093 100644 --- a/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs +++ b/TDSClient/TDSClient/TDS/Tokens/TDSTokenStreamPacketData.cs @@ -80,7 +80,7 @@ public bool Unpack(MemoryStream stream) { while (stream.Length > stream.Position) { - TDSToken token = TDSTokenFactory.ReadTokenFromStream(stream); + TDSToken token = TDSTokenFactory.ReadTokenFromStream(stream, this.Tokens); if (token != null) { this.Tokens.Add(token); diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs new file mode 100644 index 0000000..721e075 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeInfo.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using TDSClient.TDS.Interfaces; + +namespace TDSClient.TDS.Tokens.Type +{ + public class SqlTypeInfo : IPackageable + { + public SqlTypes Type { get; set; } + + public bool IsNullLen => Type == SqlTypes.NULLTYPE; + + public bool IsFixedLen => SqlTypes.FIXEDLENTYPE.HasFlag(Type); + + public bool IsVarLenType => SqlTypes.VARLENTYPE.HasFlag(Type); + + public bool IsPartLenType => SqlTypes.PARTLENTYPE.HasFlag(Type); + + + public void Pack(MemoryStream stream) + { + throw new NotImplementedException(); + } + + public bool Unpack(MemoryStream stream) + { + Type = (SqlTypes)stream.ReadByte(); + return true; + } + + public int GetFixedLen() + { + switch (Type) + { + case SqlTypes.INT1TYPE: + case SqlTypes.BITTYPE: + return 1; + + case SqlTypes.INT2TYPE: + return 2; + + case SqlTypes.INT4TYPE: + case SqlTypes.DATETIM4TYPE: + case SqlTypes.FLT4TYPE: + case SqlTypes.MONEY4TYPE: + return 4; + + case SqlTypes.MONEYTYPE: + case SqlTypes.DATETIMETYPE: + case SqlTypes.FLT8TYPE: + case SqlTypes.INT8TYPE: + return 8; + + default: + throw new ArgumentException("Unknown fixed length type"); + } + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs new file mode 100644 index 0000000..2ea1902 --- /dev/null +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypeValueFactory.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using TDSClient.TDS.Utilities; + +namespace TDSClient.TDS.Tokens.Type +{ + public static class SqlTypeValueFactory + { + public static object ReadVaue(MemoryStream stream, SqlTypeInfo type) + { + if (type.IsNullLen) + { + return null; + } + + if(type.IsFixedLen) + { + return ReadFixedTypeValue(stream, type); + } + + throw new NotSupportedException(); + } + + private static object ReadFixedTypeValue(MemoryStream stream, SqlTypeInfo type) + { + byte[] buffer; + switch (type.Type) + { + case SqlTypes.INT1TYPE: + return (sbyte)stream.ReadByte(); + + case SqlTypes.BITTYPE: + return (byte)stream.ReadByte(); + + case SqlTypes.INT2TYPE: + return (short)BigEndianUtilities.ReadUShortLE(stream); + + case SqlTypes.INT4TYPE: + return (int)BigEndianUtilities.ReadUIntLE(stream); + + case SqlTypes.DATETIM4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return ReadSmalldatetimeFromBytes(buffer); + + case SqlTypes.FLT4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return BitConverter.ToSingle(buffer, 0); + + case SqlTypes.MONEY4TYPE: + buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return ReadMoney4FromBytes(buffer); + + case SqlTypes.DATETIMETYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return ReadDatetimeFromBytes(buffer); + + case SqlTypes.FLT8TYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return BitConverter.ToDouble(buffer, 0); + + case SqlTypes.INT8TYPE: + return (long)BigEndianUtilities.ReadULongLE(stream); + + case SqlTypes.MONEYTYPE: + buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return ReadMoneyFromBytes(buffer); + + default: + throw new ArgumentException("Unknown fixed length type"); + } + } + + private static DateTime ReadSmalldatetimeFromBytes(byte[] bytes) + { + if (bytes.Length != 4) + throw new ArgumentException("Invalid byte array length."); + + // First two bytes represent days since 1900-01-01 + short days = BitConverter.ToInt16(bytes, 0); + + // Next two bytes represent minutes since midnight + short minutes = BitConverter.ToInt16(bytes, 2); + + DateTime baseDate = new DateTime(1900, 1, 1); + return baseDate.AddDays(days).AddMinutes(minutes); + } + + private static decimal ReadMoneyFromBytes(byte[] bytes) + { + if (bytes.Length != 8) + throw new ArgumentException("Invalid byte array length for MONEYTYPE."); + + long value = BitConverter.ToInt64(bytes, 0); + return value / 10000m; // scaled by 10,000 for 4-digit precision + } + + private static decimal ReadMoney4FromBytes(byte[] bytes) + { + if (bytes.Length != 4) + throw new ArgumentException("Invalid byte array length for MONEY4TYPE."); + + int value = BitConverter.ToInt32(bytes, 0); + return value / 10000m; // scaled by 10,000 for 4-digit precision + } + + private static DateTime ReadDatetimeFromBytes(byte[] bytes) + { + if (bytes.Length != 8) + throw new ArgumentException("Invalid byte array length for DATETIMETYPE."); + + int days = BitConverter.ToInt32(bytes, 0); + int timeUnits = BitConverter.ToInt32(bytes, 4); // number of 300th of a second units + + DateTime baseDate = new DateTime(1900, 1, 1); + return baseDate.AddDays(days).AddMilliseconds(timeUnits * 3.333333); // Convert 300th of a second units to milliseconds + } + } +} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs similarity index 98% rename from TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs rename to TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs index 24edc92..1f7f833 100644 --- a/TDSClient/TDSClient/TDS/Tokens/Type/VarLenType.cs +++ b/TDSClient/TDSClient/TDS/Tokens/Type/SqlTypes.cs @@ -5,7 +5,7 @@ namespace TDSClient.TDS.Tokens.Type { [Flags] - public enum VarLenType: byte + public enum SqlTypes: byte { //Zero length data types NULLTYPE = 0x1F, diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs deleted file mode 100644 index 1d0c6e0..0000000 --- a/TDSClient/TDSClient/TDS/Tokens/Type/TVTypeInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using TDSClient.TDS.Interfaces; - -namespace TDSClient.TDS.Tokens.Type -{ - public class TVTypeInfo : IPackageable - { - - public VarLenType VarLenType { get; set; } - - public void Pack(MemoryStream stream) - { - throw new NotImplementedException(); - } - - public bool Unpack(MemoryStream stream) - { - throw new NotImplementedException(); - } - } -} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs deleted file mode 100644 index 98bd312..0000000 --- a/TDSClient/TDSClient/TDS/Tokens/Type/TypeInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using TDSClient.TDS.Interfaces; - -namespace TDSClient.TDS.Tokens.Type -{ - public class TypeInfo : IPackageable - { - - public void Pack(MemoryStream stream) - { - throw new NotImplementedException(); - } - - public bool Unpack(MemoryStream stream) - { - throw new NotImplementedException(); - } - } -} diff --git a/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs b/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs deleted file mode 100644 index e2a79b5..0000000 --- a/TDSClient/TDSClient/TDS/Tokens/Type/TypeVarLen.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TDSClient.TDS.Tokens.Type -{ - internal class TypeVarLen - { - } -} diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs index d415c73..3161ed7 100644 --- a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/TransactionDescriptorHeaderData.cs @@ -17,8 +17,8 @@ internal TransactionDescriptorHeaderData() public TransactionDescriptorHeaderData(uint id) { - TransactionDescriptor = id; - OutstandingRequestCount = 0; + TransactionDescriptor = 0; + OutstandingRequestCount = id; } public ulong TransactionDescriptor { get; set; } @@ -28,8 +28,8 @@ public TransactionDescriptorHeaderData(uint id) public void Pack(MemoryStream stream) { - BigEndianUtilities.WriteULong(stream, TransactionDescriptor); - BigEndianUtilities.WriteUInt(stream, OutstandingRequestCount); + BigEndianUtilities.WriteULongLE(stream, TransactionDescriptor); + BigEndianUtilities.WriteUIntLE(stream, OutstandingRequestCount); } public bool Unpack(MemoryStream stream) diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs index db08bd4..46cad24 100644 --- a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlAllHeadersPacketData.cs @@ -15,7 +15,7 @@ public class SqlAllHeadersPacketData : IPackageable public void Pack(MemoryStream stream) { - BigEndianUtilities.WriteUInt(stream, TotalLength); + BigEndianUtilities.WriteUIntLE(stream, TotalLength); foreach (var header in Headers) { header.Pack(stream); diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs index 8762c27..dc9930b 100644 --- a/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/SqlHeaderPacketData.cs @@ -24,8 +24,8 @@ public SqlHeaderPacketData(ISqlHeaderData data) public void Pack(MemoryStream stream) { - BigEndianUtilities.WriteUInt(stream, Size); - BigEndianUtilities.WriteUShort(stream, (ushort)Data.HeaderType); + BigEndianUtilities.WriteUIntLE(stream, Size); + BigEndianUtilities.WriteUShortLE(stream, (ushort)Data.HeaderType); Data.Pack(stream); } diff --git a/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs index 3edbc9a..ed96055 100644 --- a/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs +++ b/TDSClient/TDSClient/TDS/Tranasction/TDSSqlBatchPacketData.cs @@ -32,11 +32,13 @@ public bool Equals(TDSSqlBatchPacketData other) throw new NotImplementedException(); } - public ushort Length() => (ushort)(AllHeaders.TotalLength + SqlText.Length * sizeof(char)); + public ushort Length() => (ushort)(AllHeaders.TotalLength + SqlText.Length * sizeof(char) + sizeof(ulong)); public void Pack(MemoryStream stream) { AllHeaders.Pack(stream); + //Enclave can be empty stream + //BigEndianUtilities.WriteULong(stream, 0UL); BigEndianUtilities.WriteUnicodeStream(stream, SqlText); } diff --git a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs index 017aba4..61effdc 100644 --- a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs +++ b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs @@ -26,6 +26,17 @@ public static void WriteUShort(MemoryStream stream, ushort value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteUShortLE(MemoryStream stream, ushort value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + } + /// /// Used to write value to stream in big endian order. /// @@ -39,6 +50,19 @@ public static void WriteUInt(MemoryStream stream, uint value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in big endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteUIntLE(MemoryStream stream, uint value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 24)); + } + /// /// Used to write value to stream in big endian order. /// @@ -56,6 +80,23 @@ public static void WriteULong(MemoryStream stream, ulong value) stream.WriteByte((byte)value); } + /// + /// Used to write value to stream in little endian order. + /// + /// MemoryStream to to write the value to. + /// Value to write to MemoryStream. + public static void WriteULongLE(MemoryStream stream, ulong value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 24)); + stream.WriteByte((byte)(value >> 32)); + stream.WriteByte((byte)(value >> 40)); + stream.WriteByte((byte)(value >> 48)); + stream.WriteByte((byte)(value >> 56)); + } + /// /// Used to write byte array to stream in big endian order. /// @@ -78,7 +119,7 @@ public static void WriteUnicodeStream(MemoryStream stream, IEnumerable cha { foreach (char c in chars) { - WriteUShort(stream, c); + WriteUShortLE(stream, c); } } @@ -100,6 +141,22 @@ public static ushort ReadUShort(MemoryStream stream) return result; } + /// + /// Used to read a UShort value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// UShort value read from the stream. + public static ushort ReadUShortLE(MemoryStream stream) + { + ushort result = 0; + for (int i = 0; i < 2; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a UInt value from stream in big endian order. /// @@ -117,6 +174,22 @@ public static uint ReadUInt(MemoryStream stream) return result; } + /// + /// Used to read a UInt value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// UInt value read from the stream. + public static uint ReadUIntLE(MemoryStream stream) + { + uint result = 0; + for (int i = 0; i < 4; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a ULong value from stream in big endian order. /// @@ -134,6 +207,22 @@ public static ulong ReadULong(MemoryStream stream) return result; } + /// + /// Used to read a ULong value from stream in little endian order. + /// + /// MemoryStream from which to read the value. + /// ULong value read from the stream. + public static ulong ReadULongLE(MemoryStream stream) + { + ulong result = 0; + for (int i = 0; i < 8; i++) + { + result |= (byte)(Convert.ToByte(stream.ReadByte()) << 8 * i); + } + + return result; + } + /// /// Used to read a byte array from stream in big endian order. /// @@ -163,7 +252,7 @@ public static char[] ReadUnicodeStream(MemoryStream stream, int length) //TODO: Check order to read in for (int i = 1; i <= length; i++) { - result[length - i] = (char)ReadUShort(stream); + result[length - i] = (char)ReadUShortLE(stream); } return result; diff --git a/TDSClient/TDSClientLiveTest/Program.cs b/TDSClient/TDSClientLiveTest/Program.cs index 8bb55bd..f778a66 100644 --- a/TDSClient/TDSClientLiveTest/Program.cs +++ b/TDSClient/TDSClientLiveTest/Program.cs @@ -16,13 +16,13 @@ class Program static void Main(string[] args) { - TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database, SslProtocols.None); + TDSSQLTestClient tdsClient = new TDSSQLTestClient(Server, Port, Username, Password, Database, SslProtocols.Tls12); TDSClient.TDS.Utilities.LoggingUtilities.SetVerboseLog(Console.Out); for (int i = 0; i < 1; i++) { tdsClient.Connect(); - tdsClient.Query("select 1"); + tdsClient.Query("SELECT GETUTCDATE() AS CurrentUTCDateTime"); tdsClient.Disconnect(); Console.WriteLine(); Thread.Sleep(1000); diff --git a/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj b/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj index b223a2c..6d137a3 100644 --- a/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj +++ b/TDSClient/TDSClientLiveTest/TDSClientLiveTest.csproj @@ -2,7 +2,8 @@ Exe - netcoreapp3.1 + net60 + latest From 9e47dea4c1a56e0fb7c37cbd92c6e1a20b923803 Mon Sep 17 00:00:00 2001 From: JPaja Date: Tue, 19 Sep 2023 14:23:08 +0200 Subject: [PATCH 5/7] Fix: Unicode stream read + Print of query data --- .../TDSClient/TDS/Client/TDSSQLTestClient.cs | 23 +++++++++++++++++++ .../TDSClient/TDS/Tokens/Cols/ColMetadata.cs | 2 +- .../TDS/Tokens/Cols/TDSSqlReturnValueToken.cs | 2 +- .../Headers/QueryNotificationsHeaderData.cs | 4 ++-- .../TDS/Utilities/BigEndianUtilities.cs | 8 +++---- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index 7e3c23d..2f9dfcb 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -20,6 +20,7 @@ namespace TDSClient.TDS.Client using TDSClient.TDS.PreLogin; using TDSClient.TDS.Query; using TDSClient.TDS.Tokens; + using TDSClient.TDS.Tokens.Cols; using TDSClient.TDS.Tranasction.Header.Headers; using TDSClient.TDS.Tranasction.Headers; using TDSClient.TDS.Utilities; @@ -270,6 +271,28 @@ private void PrintTdsToken(TDSToken token) LoggingUtilities.WriteLog($" ProcName: {infoToken.ProcName}"); LoggingUtilities.WriteLog($" LineNumber: {infoToken.LineNumber}"); } + else if (token is TDSColMetadataToken colMetadataToken) + { + LoggingUtilities.WriteLog($" Client Column Metadata Info token:"); + + LoggingUtilities.WriteLog($" Columns: {colMetadataToken.Count}"); + for (int i = 0; i < colMetadataToken.Metadata.Length; i++) + { + var metadata = colMetadataToken.Metadata[i]; + LoggingUtilities.WriteLog($" Index: {i}"); + LoggingUtilities.WriteLog($" Name: {metadata.ColumnName}"); + LoggingUtilities.WriteLog($" Type: {metadata.Type.Type}"); + LoggingUtilities.WriteLog(""); + } + } + else if (token is TDSRowToken rowToken) + { + LoggingUtilities.WriteLog($" Client Row data token:"); + for(int i = 0; i < rowToken.Values.Length; i++) + { + LoggingUtilities.WriteLog($" Row [{i}]: {rowToken.Values[i]?.ToString() ?? "NUll"}"); + } + } } /// diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs index 26514f0..eefbd1a 100644 --- a/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/ColMetadata.cs @@ -52,7 +52,7 @@ public override bool Unpack(MemoryStream stream) var colNameLen = stream.ReadByte(); - var colName = BigEndianUtilities.ReadUnicodeStream(stream, colNameLen); + var colName = BigEndianUtilities.ReadUnicodeStreamLE(stream, colNameLen); ColumnName = new string(colName); return true; diff --git a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs index 16cf8b4..aa6dc52 100644 --- a/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs +++ b/TDSClient/TDSClient/TDS/Tokens/Cols/TDSSqlReturnValueToken.cs @@ -41,7 +41,7 @@ public override bool Unpack(MemoryStream stream) { Index = BigEndianUtilities.ReadUShortLE(stream); var paraeterNameLen = stream.ReadByte(); - var paraeterName = BigEndianUtilities.ReadUnicodeStream(stream, paraeterNameLen); + var paraeterName = BigEndianUtilities.ReadUnicodeStreamLE(stream, paraeterNameLen); ParameterName = new string(paraeterName); Status = Convert.ToByte(stream.ReadByte()); diff --git a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs index 6355d60..fc368cd 100644 --- a/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs +++ b/TDSClient/TDSClient/TDS/Tranasction/Header/Headers/QueryNotificationsHeaderData.cs @@ -24,11 +24,11 @@ public void Pack(MemoryStream stream) public bool Unpack(MemoryStream stream) { var notifyIdLen = BigEndianUtilities.ReadUShort(stream); - var notifyIdChars = BigEndianUtilities.ReadUnicodeStream(stream, notifyIdLen); + var notifyIdChars = BigEndianUtilities.ReadUnicodeStreamLE(stream, notifyIdLen); NotifyId = new string(notifyIdChars); var ssbDeploymentLen = BigEndianUtilities.ReadUShort(stream); - var ssbDeploymentChars = BigEndianUtilities.ReadUnicodeStream(stream, ssbDeploymentLen); + var ssbDeploymentChars = BigEndianUtilities.ReadUnicodeStreamLE(stream, ssbDeploymentLen); SSBDeployment = new string(ssbDeploymentChars); NotifyTimeout = BigEndianUtilities.ReadULong(stream); diff --git a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs index 61effdc..7b2bf04 100644 --- a/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs +++ b/TDSClient/TDSClient/TDS/Utilities/BigEndianUtilities.cs @@ -241,18 +241,18 @@ public static byte[] ReadByteArray(MemoryStream stream, uint length) } /// - /// Used to read a Unicode char array from stream in big endian order. + /// Used to read a Unicode char array from stream in little endian order. /// /// MemoryStream from which to read the unicode char array. /// Length of the unicode array to read. /// Unicode char array read from the stream. - public static char[] ReadUnicodeStream(MemoryStream stream, int length) + public static char[] ReadUnicodeStreamLE(MemoryStream stream, int length) { char[] result = new char[length]; //TODO: Check order to read in - for (int i = 1; i <= length; i++) + for (int i = 0; i < length; i++) { - result[length - i] = (char)ReadUShortLE(stream); + result[i] = (char)ReadUShortLE(stream); } return result; From 703647bc32c2cd97ddd5f24b4e032e3fdc1a1ccf Mon Sep 17 00:00:00 2001 From: JPaja Date: Tue, 19 Sep 2023 14:25:01 +0200 Subject: [PATCH 6/7] Fix query return type --- TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index 2f9dfcb..ef548a8 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -366,7 +366,7 @@ public void Connect() } public int queryCount = 0; - public Dictionary Query(string query) + public void Query(string query) { var id = Interlocked.Increment(ref queryCount); try @@ -398,7 +398,6 @@ public Dictionary Query(string query) LoggingUtilities.WriteLog($"InnerException: {ex.InnerException.Message}"); } } - return null; } private void MeasureDNSResolutionTime() From 3095658ced531a15da67f346a899a20ab25d0677 Mon Sep 17 00:00:00 2001 From: JPaja Date: Tue, 19 Sep 2023 15:59:44 +0200 Subject: [PATCH 7/7] Add: Return type on query --- .../TDSClient/TDS/Client/TDSSQLTestClient.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs index ef548a8..47e3212 100644 --- a/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs +++ b/TDSClient/TDSClient/TDS/Client/TDSSQLTestClient.cs @@ -366,9 +366,11 @@ public void Connect() } public int queryCount = 0; - public void Query(string query) + public (string[], object[][]) Query(string query) { var id = Interlocked.Increment(ref queryCount); + string[] colNames = Array.Empty(); + List result = new List(); try { var request = new TDSSqlBatchPacketData(query); @@ -382,6 +384,21 @@ public void Query(string query) foreach (var token in response.Tokens) { PrintTdsToken(token); + + if (token is TDSColMetadataToken colMetadataToken) + { + colNames = new string[colMetadataToken.Count]; + for (int i = 0; i < colMetadataToken.Metadata.Length; i++) + { + var metadata = colMetadataToken.Metadata[i]; + colNames[i] = metadata.ColumnName; + } + } + else if (token is TDSRowToken rowToken) + { + LoggingUtilities.WriteLog($" Client Row data token:"); + result.Add(rowToken.Values); + } } } else @@ -398,6 +415,7 @@ public void Query(string query) LoggingUtilities.WriteLog($"InnerException: {ex.InnerException.Message}"); } } + return (colNames, result.ToArray()); } private void MeasureDNSResolutionTime()