966bba02bb
Former-commit-id: bb0468d0f257ff100aa895eb5fe583fb5dfbf900
909 lines
23 KiB
C#
909 lines
23 KiB
C#
//
|
|
// Mono.Data.Tds.Protocol.TdsComm.cs
|
|
//
|
|
// Author:
|
|
// Tim Coleman (tim@timcoleman.com)
|
|
// Gonzalo Paniagua Javier (gonzalo@novell.com)
|
|
//
|
|
// Copyright (C) 2002 Tim Coleman
|
|
// Copyright (c) 2009 Novell, Inc.
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace Mono.Data.Tds.Protocol {
|
|
internal sealed class TdsComm
|
|
{
|
|
#region Fields
|
|
|
|
NetworkStream stream;
|
|
int packetSize;
|
|
TdsPacketType packetType = TdsPacketType.None;
|
|
bool connReset;
|
|
Encoding encoder;
|
|
|
|
string dataSource;
|
|
int commandTimeout;
|
|
|
|
byte[] outBuffer;
|
|
int outBufferLength;
|
|
int nextOutBufferIndex = 0;
|
|
bool lsb;
|
|
|
|
byte[] inBuffer;
|
|
int inBufferLength;
|
|
int inBufferIndex = 0;
|
|
|
|
static int headerLength = 8;
|
|
|
|
byte[] tmpBuf = new byte[8];
|
|
byte[] resBuffer = new byte[256];
|
|
|
|
int packetsSent;
|
|
int packetsReceived = 0;
|
|
|
|
Socket socket;
|
|
TdsVersion tdsVersion;
|
|
|
|
#endregion // Fields
|
|
|
|
#region Constructors
|
|
|
|
public TdsComm (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
|
|
{
|
|
this.packetSize = packetSize;
|
|
this.tdsVersion = tdsVersion;
|
|
this.dataSource = dataSource;
|
|
|
|
outBuffer = new byte[packetSize];
|
|
inBuffer = new byte[packetSize];
|
|
|
|
outBufferLength = packetSize;
|
|
inBufferLength = packetSize;
|
|
|
|
lsb = true;
|
|
|
|
IPEndPoint endPoint;
|
|
bool have_exception = false;
|
|
|
|
try {
|
|
IPAddress ip;
|
|
if(IPAddress.TryParse(this.dataSource, out ip)) {
|
|
endPoint = new IPEndPoint(ip, port);
|
|
} else {
|
|
IPHostEntry hostEntry = Dns.GetHostEntry (this.dataSource);
|
|
endPoint = new IPEndPoint(hostEntry.AddressList [0], port);
|
|
}
|
|
} catch (SocketException e) {
|
|
throw new TdsInternalException ("Server does not exist or connection refused.", e);
|
|
}
|
|
|
|
try {
|
|
socket = new Socket (endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
IAsyncResult ares = socket.BeginConnect (endPoint, null, null);
|
|
int timeout_ms = timeout * 1000;
|
|
if (timeout > 0 && !ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (timeout_ms, false))
|
|
throw Tds.CreateTimeoutException (dataSource, "Open()");
|
|
socket.EndConnect (ares);
|
|
try {
|
|
// MS sets these socket option
|
|
socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
|
|
} catch (SocketException) {
|
|
// Some platform may throw an exception, so
|
|
// eat all socket exception, yeaowww!
|
|
}
|
|
|
|
try {
|
|
socket.NoDelay = true;
|
|
socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout_ms);
|
|
socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout_ms);
|
|
} catch {
|
|
// Ignore exceptions here for systems that do not support these options.
|
|
}
|
|
// Let the stream own the socket and take the pleasure of closing it
|
|
stream = new NetworkStream (socket, true);
|
|
} catch (SocketException e) {
|
|
have_exception = true;
|
|
throw new TdsInternalException ("Server does not exist or connection refused.", e);
|
|
} catch (Exception) {
|
|
have_exception = true;
|
|
throw;
|
|
} finally {
|
|
if (have_exception && socket != null) {
|
|
try {
|
|
Socket s = socket;
|
|
socket = null;
|
|
s.Close ();
|
|
} catch {}
|
|
}
|
|
}
|
|
if (!socket.Connected)
|
|
throw new TdsInternalException ("Server does not exist or connection refused.", null);
|
|
packetsSent = 1;
|
|
}
|
|
|
|
#endregion // Constructors
|
|
|
|
#region Properties
|
|
|
|
public int CommandTimeout {
|
|
get { return commandTimeout; }
|
|
set { commandTimeout = value; }
|
|
}
|
|
|
|
internal Encoding Encoder {
|
|
get { return encoder; }
|
|
set { encoder = value; }
|
|
}
|
|
|
|
public int PacketSize {
|
|
get { return packetSize; }
|
|
set { packetSize = value; }
|
|
}
|
|
|
|
public bool TdsByteOrder {
|
|
get { return !lsb; }
|
|
set { lsb = !value; }
|
|
}
|
|
#endregion // Properties
|
|
|
|
#region Methods
|
|
|
|
public byte[] Swap(byte[] toswap) {
|
|
byte[] ret = new byte[toswap.Length];
|
|
for(int i = 0; i < toswap.Length; i++)
|
|
ret [toswap.Length - i - 1] = toswap[i];
|
|
|
|
return ret;
|
|
}
|
|
|
|
public void SendIfFull ()
|
|
{
|
|
if (nextOutBufferIndex == outBufferLength) {
|
|
SendPhysicalPacket (false);
|
|
nextOutBufferIndex = headerLength;
|
|
}
|
|
}
|
|
|
|
public void SendIfFull (int reserve)
|
|
{
|
|
if (nextOutBufferIndex+reserve > outBufferLength) {
|
|
SendPhysicalPacket (false);
|
|
nextOutBufferIndex = headerLength;
|
|
}
|
|
}
|
|
|
|
public void Append (object o)
|
|
{
|
|
if (o == null || o == DBNull.Value) {
|
|
Append ((byte)0);
|
|
return ;
|
|
}
|
|
|
|
switch (Type.GetTypeCode (o.GetType ())) {
|
|
case TypeCode.Byte :
|
|
Append ((byte) o);
|
|
return;
|
|
case TypeCode.Boolean:
|
|
if ((bool)o == true)
|
|
Append ((byte)1);
|
|
else
|
|
Append ((byte)0);
|
|
return;
|
|
case TypeCode.Object :
|
|
if (o is byte[])
|
|
Append ((byte[]) o);
|
|
else if (o is Guid)
|
|
Append (((Guid) o).ToByteArray ());
|
|
else break;
|
|
return;
|
|
case TypeCode.Int16 :
|
|
Append ((short) o);
|
|
return;
|
|
case TypeCode.Int32 :
|
|
Append ((int) o);
|
|
return;
|
|
case TypeCode.String :
|
|
Append ((string) o);
|
|
return;
|
|
case TypeCode.Double :
|
|
Append ((double) o);
|
|
return;
|
|
case TypeCode.Single :
|
|
Append ((float) o);
|
|
return;
|
|
case TypeCode.Int64 :
|
|
Append ((long) o);
|
|
return;
|
|
case TypeCode.Decimal:
|
|
Append ((decimal) o, 17);
|
|
return;
|
|
case TypeCode.DateTime:
|
|
Append ((DateTime) o, 8);
|
|
return;
|
|
}
|
|
throw new InvalidOperationException (String.Format ("Object Type :{0} , not being appended", o.GetType ()));
|
|
}
|
|
|
|
public void Append (byte b)
|
|
{
|
|
SendIfFull ();
|
|
Store (nextOutBufferIndex, b);
|
|
nextOutBufferIndex++;
|
|
}
|
|
|
|
public void Append (DateTime t, int bytes)
|
|
{
|
|
DateTime epoch = new DateTime (1900,1,1);
|
|
|
|
TimeSpan span = t - epoch; //new TimeSpan (t.Ticks - epoch.Ticks);
|
|
int days, hours, minutes, secs;
|
|
long msecs;
|
|
int val = 0;
|
|
|
|
days = span.Days;
|
|
hours = span.Hours;
|
|
minutes = span.Minutes;
|
|
secs = span.Seconds;
|
|
msecs = span.Milliseconds;
|
|
|
|
if (epoch > t) {
|
|
// If t.Hour/Min/Sec/MSec is > 0, days points to the next day and hence,
|
|
// we move it back by a day - otherwise, no change
|
|
days = (t.Hour > 0 || t.Minute > 0 || t.Second > 0 || t.Millisecond > 0) ? days-1: days;
|
|
hours = t.Hour;
|
|
minutes = t.Minute;
|
|
secs = t.Second;
|
|
msecs = t.Millisecond;
|
|
}
|
|
|
|
SendIfFull (bytes);
|
|
if (bytes == 8) {
|
|
long ms = (hours * 3600 + minutes * 60 + secs)*1000L + (long)msecs;
|
|
val = (int) ((ms*300)/1000);
|
|
AppendInternal ((int) days);
|
|
AppendInternal ((int) val);
|
|
} else if (bytes ==4) {
|
|
val = span.Hours * 60 + span.Minutes;
|
|
AppendInternal ((short) days);
|
|
AppendInternal ((short) val);
|
|
} else {
|
|
throw new Exception ("Invalid No of bytes");
|
|
}
|
|
}
|
|
|
|
public void Append (byte[] b)
|
|
{
|
|
Append (b, b.Length, (byte) 0);
|
|
}
|
|
|
|
|
|
public void Append (byte[] b, int len, byte pad)
|
|
{
|
|
int bufBytesToCopy = System.Math.Min (b.Length, len);
|
|
int padBytesToCopy = len - bufBytesToCopy;
|
|
int bufPos = 0;
|
|
|
|
/* copy out of our input buffer in the largest chunks possible *
|
|
* at a time. limited only by the buffer size for our outgoing *
|
|
* packets. */
|
|
|
|
while (bufBytesToCopy > 0)
|
|
{
|
|
SendIfFull ();
|
|
|
|
int availBytes = outBufferLength - nextOutBufferIndex;
|
|
int bufSize = System.Math.Min (availBytes, bufBytesToCopy);
|
|
|
|
Buffer.BlockCopy (b, bufPos, outBuffer, nextOutBufferIndex, bufSize);
|
|
|
|
nextOutBufferIndex += bufSize;
|
|
bufBytesToCopy -= bufSize;
|
|
bufPos += bufSize;
|
|
}
|
|
|
|
while (padBytesToCopy > 0)
|
|
{
|
|
SendIfFull ();
|
|
|
|
int availBytes = outBufferLength - nextOutBufferIndex;
|
|
int bufSize = System.Math.Min (availBytes, padBytesToCopy);
|
|
|
|
for (int i = 0; i < bufSize; i++)
|
|
outBuffer [nextOutBufferIndex++] = pad;
|
|
|
|
padBytesToCopy -= bufSize;
|
|
}
|
|
}
|
|
|
|
private void AppendInternal (short s)
|
|
{
|
|
if (!lsb) {
|
|
outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
|
|
outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
|
|
} else {
|
|
outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
|
|
outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
|
|
}
|
|
}
|
|
|
|
public void Append (short s)
|
|
{
|
|
SendIfFull (sizeof (short));
|
|
AppendInternal (s);
|
|
}
|
|
|
|
public void Append (ushort s)
|
|
{
|
|
SendIfFull (sizeof (short));
|
|
AppendInternal ((short) s);
|
|
}
|
|
|
|
private void AppendInternal (int i)
|
|
{
|
|
if (!lsb) {
|
|
AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
|
|
AppendInternal ((short) ((short) (i & 0xffff)));
|
|
} else {
|
|
AppendInternal ((short) ((short) (i & 0xffff)));
|
|
AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
|
|
}
|
|
}
|
|
|
|
public void Append (int i)
|
|
{
|
|
SendIfFull (sizeof (int));
|
|
AppendInternal (i);
|
|
}
|
|
|
|
public void Append (string s)
|
|
{
|
|
if (tdsVersion < TdsVersion.tds70) {
|
|
Append (encoder.GetBytes (s));
|
|
} else {
|
|
for (int i = 0; i < s.Length; i++) {
|
|
SendIfFull (sizeof(short));
|
|
AppendInternal ((short)s[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AppendNonUnicode (string s)
|
|
{
|
|
if (tdsVersion < TdsVersion.tds70) {
|
|
Append (encoder.GetBytes (s));
|
|
} else {
|
|
for (int i = 0; i < s.Length; i++) {
|
|
SendIfFull (sizeof(byte));
|
|
Append ((byte)s[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Appends with padding
|
|
public byte[] Append (string s, int len, byte pad)
|
|
{
|
|
if (s == null)
|
|
return new byte[0];
|
|
|
|
byte[] result = encoder.GetBytes (s);
|
|
Append (result, len, pad);
|
|
return result;
|
|
}
|
|
|
|
public void Append (double value)
|
|
{
|
|
if (!lsb)
|
|
Append (Swap (BitConverter.GetBytes (value)), sizeof(double), (byte)0);
|
|
else
|
|
Append (BitConverter.GetBytes (value), sizeof(double), (byte)0);
|
|
}
|
|
|
|
public void Append (float value)
|
|
{
|
|
if (!lsb)
|
|
Append (Swap (BitConverter.GetBytes (value)), sizeof(float), (byte)0);
|
|
else
|
|
Append (BitConverter.GetBytes (value), sizeof(float), (byte)0);
|
|
}
|
|
|
|
public void Append (long l)
|
|
{
|
|
SendIfFull (sizeof (long));
|
|
if (!lsb) {
|
|
AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
|
|
AppendInternal ((int) ((int) (l & 0xffffffff)));
|
|
} else {
|
|
AppendInternal ((int) ((int) (l & 0xffffffff)));
|
|
AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
|
|
}
|
|
}
|
|
|
|
public void Append (decimal d, int bytes)
|
|
{
|
|
int[] arr = Decimal.GetBits (d);
|
|
byte sign = (d > 0 ? (byte)1 : (byte)0);
|
|
SendIfFull (bytes);
|
|
Append (sign);
|
|
AppendInternal (arr[0]);
|
|
AppendInternal (arr[1]);
|
|
AppendInternal (arr[2]);
|
|
AppendInternal ((int)0);
|
|
}
|
|
|
|
public void AppendMoney (decimal d, int size)
|
|
{
|
|
// The method for this is to simply multiply by 10^4 and then stuff
|
|
// the value into either a int or long value depending on the size
|
|
|
|
SendIfFull (size);
|
|
|
|
decimal tmpD = Decimal.Multiply(d, 10000m);
|
|
if (size > 4) {
|
|
long longValue = Decimal.ToInt64(tmpD);
|
|
|
|
int significantHalf = (int) ((longValue >> 32) & 0xffffffff);
|
|
int lessSignificantHalf = (int)(longValue & 0xffffffff);
|
|
|
|
AppendInternal (significantHalf);
|
|
AppendInternal (lessSignificantHalf);
|
|
} else {
|
|
int intValue = Decimal.ToInt32(tmpD);
|
|
AppendInternal (intValue);
|
|
}
|
|
}
|
|
|
|
// A method for decimals that properly scales the decimal out before putting on the TDS steam
|
|
public void AppendDecimal (decimal d, int bytes, int scale)
|
|
{
|
|
decimal tmpD1 = Decimal.Multiply (d, (decimal)System.Math.Pow (10.0, scale));
|
|
decimal tmpD2 = System.Math.Abs(Decimal.Truncate (tmpD1));
|
|
|
|
int[] arr = Decimal.GetBits (tmpD2);
|
|
byte sign = (d > 0 ? (byte)1 : (byte)0);
|
|
SendIfFull (bytes);
|
|
Append (sign);
|
|
AppendInternal (arr[0]);
|
|
AppendInternal (arr[1]);
|
|
AppendInternal (arr[2]);
|
|
AppendInternal ((int)0);
|
|
}
|
|
|
|
public void Close ()
|
|
{
|
|
if (stream == null)
|
|
return;
|
|
|
|
connReset = false;
|
|
socket = null;
|
|
try {
|
|
stream.Close ();
|
|
} catch {
|
|
}
|
|
stream = null;
|
|
}
|
|
|
|
public bool IsConnected ()
|
|
{
|
|
return socket != null && socket.Connected && !(socket.Poll (0, SelectMode.SelectRead) && socket.Available == 0);
|
|
}
|
|
|
|
public byte GetByte ()
|
|
{
|
|
byte result;
|
|
|
|
if (inBufferIndex >= inBufferLength) {
|
|
// out of data, read another physical packet.
|
|
GetPhysicalPacket ();
|
|
}
|
|
result = inBuffer[inBufferIndex++];
|
|
return result;
|
|
}
|
|
|
|
public byte[] GetBytes (int len, bool exclusiveBuffer)
|
|
{
|
|
byte[] result = null;
|
|
int i;
|
|
|
|
// Do not keep an internal result buffer larger than 16k.
|
|
// This would unnecessarily use up memory.
|
|
if (exclusiveBuffer || len > 16384)
|
|
result = new byte[len];
|
|
else
|
|
{
|
|
if (resBuffer.Length < len)
|
|
resBuffer = new byte[len];
|
|
result = resBuffer;
|
|
}
|
|
|
|
for (i = 0; i<len; )
|
|
{
|
|
if (inBufferIndex >= inBufferLength)
|
|
GetPhysicalPacket ();
|
|
|
|
int avail = inBufferLength - inBufferIndex;
|
|
avail = avail>len-i ? len-i : avail;
|
|
|
|
Buffer.BlockCopy (inBuffer, inBufferIndex, result, i, avail);
|
|
i += avail;
|
|
inBufferIndex += avail;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public string GetString (int len, Encoding enc)
|
|
{
|
|
if (tdsVersion >= TdsVersion.tds70)
|
|
return GetString (len, true, null);
|
|
else
|
|
return GetString (len, false, null);
|
|
}
|
|
|
|
public string GetString (int len)
|
|
{
|
|
if (tdsVersion >= TdsVersion.tds70)
|
|
return GetString (len, true);
|
|
else
|
|
return GetString (len, false);
|
|
}
|
|
|
|
public string GetString (int len, bool wide, Encoding enc)
|
|
{
|
|
if (wide) {
|
|
char[] chars = new char[len];
|
|
for (int i = 0; i < len; ++i) {
|
|
int lo = ((byte) GetByte ()) & 0xFF;
|
|
int hi = ((byte) GetByte ()) & 0xFF;
|
|
chars[i] = (char) (lo | ( hi << 8));
|
|
}
|
|
return new String (chars);
|
|
}
|
|
else {
|
|
byte[] result = new byte[len];
|
|
Array.Copy (GetBytes (len, false), result, len);
|
|
// Use the passed encoder, if available
|
|
if (enc != null)
|
|
return (enc.GetString (result));
|
|
else
|
|
return (encoder.GetString (result));
|
|
}
|
|
}
|
|
|
|
public string GetString (int len, bool wide)
|
|
{
|
|
return GetString (len, wide, null);
|
|
}
|
|
|
|
public int GetNetShort ()
|
|
{
|
|
byte[] tmp = new byte[2];
|
|
tmp[0] = GetByte ();
|
|
tmp[1] = GetByte ();
|
|
return Ntohs (tmp, 0);
|
|
}
|
|
|
|
public short GetTdsShort ()
|
|
{
|
|
byte[] input = new byte[2];
|
|
|
|
for (int i = 0; i < 2; i += 1)
|
|
input[i] = GetByte ();
|
|
if(!BitConverter.IsLittleEndian)
|
|
return (BitConverter.ToInt16 (Swap (input), 0));
|
|
else
|
|
return (BitConverter.ToInt16 (input, 0));
|
|
}
|
|
|
|
|
|
public int GetTdsInt ()
|
|
{
|
|
byte[] input = new byte[4];
|
|
for (int i = 0; i < 4; i += 1) {
|
|
input[i] = GetByte ();
|
|
}
|
|
if(!BitConverter.IsLittleEndian)
|
|
return (BitConverter.ToInt32 (Swap (input), 0));
|
|
else
|
|
return (BitConverter.ToInt32 (input, 0));
|
|
}
|
|
|
|
public long GetTdsInt64 ()
|
|
{
|
|
byte[] input = new byte[8];
|
|
for (int i = 0; i < 8; i += 1)
|
|
input[i] = GetByte ();
|
|
if(!BitConverter.IsLittleEndian)
|
|
return (BitConverter.ToInt64 (Swap (input), 0));
|
|
else
|
|
return (BitConverter.ToInt64 (input, 0));
|
|
}
|
|
|
|
private void GetPhysicalPacket ()
|
|
{
|
|
int dataLength = GetPhysicalPacketHeader ();
|
|
GetPhysicalPacketData (dataLength);
|
|
}
|
|
|
|
int Read (byte [] buffer, int offset, int count)
|
|
{
|
|
try {
|
|
return stream.Read (buffer, offset, count);
|
|
} catch {
|
|
socket = null;
|
|
stream.Close ();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private int GetPhysicalPacketHeader ()
|
|
{
|
|
int nread = 0;
|
|
|
|
int n;
|
|
// read the header
|
|
while (nread < 8) {
|
|
n = Read (tmpBuf, nread, 8 - nread);
|
|
if (n <= 0) {
|
|
socket = null;
|
|
stream.Close ();
|
|
throw new IOException (n == 0 ? "Connection lost" : "Connection error");
|
|
}
|
|
nread += n;
|
|
}
|
|
|
|
TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
|
|
if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
|
|
{
|
|
throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
|
|
}
|
|
|
|
// figure out how many bytes are remaining in this packet.
|
|
int len = Ntohs (tmpBuf, 2) - 8;
|
|
if (len >= inBuffer.Length)
|
|
inBuffer = new byte[len];
|
|
|
|
if (len < 0) {
|
|
throw new Exception (String.Format ("Confused by a length of {0}", len));
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
private void GetPhysicalPacketData (int length)
|
|
{
|
|
// now get the data
|
|
int nread = 0;
|
|
int n;
|
|
|
|
while (nread < length) {
|
|
n = Read (inBuffer, nread, length - nread);
|
|
if (n <= 0) {
|
|
socket = null;
|
|
stream.Close ();
|
|
throw new IOException (n == 0 ? "Connection lost" : "Connection error");
|
|
}
|
|
nread += n;
|
|
}
|
|
|
|
packetsReceived++;
|
|
|
|
// adjust the bookkeeping info about the incoming buffer
|
|
inBufferLength = length;
|
|
inBufferIndex = 0;
|
|
}
|
|
|
|
|
|
private static int Ntohs (byte[] buf, int offset)
|
|
{
|
|
int lo = ((int) buf[offset + 1] & 0xff);
|
|
int hi = (((int) buf[offset] & 0xff ) << 8);
|
|
|
|
return hi | lo;
|
|
// return an int since we really want an _unsigned_
|
|
}
|
|
|
|
public byte Peek ()
|
|
{
|
|
// If out of data, read another physical packet.
|
|
if (inBufferIndex >= inBufferLength)
|
|
GetPhysicalPacket ();
|
|
|
|
return inBuffer[inBufferIndex];
|
|
}
|
|
|
|
public bool Poll (int seconds, SelectMode selectMode)
|
|
{
|
|
return Poll (socket, seconds, selectMode);
|
|
}
|
|
|
|
private bool Poll (Socket s, int seconds, SelectMode selectMode)
|
|
{
|
|
long uSeconds = seconds * 1000000;
|
|
bool bState = false;
|
|
|
|
while (uSeconds > (long) Int32.MaxValue) {
|
|
bState = s.Poll (Int32.MaxValue, selectMode);
|
|
if (bState)
|
|
return true;
|
|
uSeconds -= Int32.MaxValue;
|
|
}
|
|
return s.Poll ((int) uSeconds, selectMode);
|
|
}
|
|
|
|
internal void ResizeOutBuf (int newSize)
|
|
{
|
|
if (newSize != outBufferLength) {
|
|
byte[] newBuf = new byte [newSize];
|
|
Buffer.BlockCopy (outBuffer, 0, newBuf, 0, newSize);
|
|
outBufferLength = newSize;
|
|
outBuffer = newBuf;
|
|
}
|
|
}
|
|
|
|
public bool ResetConnection {
|
|
get { return connReset; }
|
|
set { connReset = value; }
|
|
}
|
|
|
|
public void SendPacket ()
|
|
{
|
|
// Reset connection flag is only valid for SQLBatch/RPC/DTC messages
|
|
if (packetType != TdsPacketType.Query && packetType != TdsPacketType.RPC)
|
|
connReset = false;
|
|
|
|
SendPhysicalPacket (true);
|
|
nextOutBufferIndex = 0;
|
|
packetType = TdsPacketType.None;
|
|
// Reset connection-reset flag to false - as any exception would anyway close
|
|
// the whole connection
|
|
connReset = false;
|
|
packetsSent = 1;
|
|
}
|
|
|
|
private void SendPhysicalPacket (bool isLastSegment)
|
|
{
|
|
if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
|
|
byte status = (byte) ((isLastSegment ? 0x01 : 0x00) | (connReset ? 0x08 : 0x00));
|
|
|
|
// packet type
|
|
Store (0, (byte) packetType);
|
|
Store (1, status);
|
|
Store (2, (short) nextOutBufferIndex );
|
|
Store (4, (byte) 0);
|
|
Store (5, (byte) 0);
|
|
if (tdsVersion >= TdsVersion.tds70)
|
|
Store (6, (byte) packetsSent);
|
|
else
|
|
Store (6, (byte) 0);
|
|
Store (7, (byte) 0);
|
|
|
|
stream.Write (outBuffer, 0, nextOutBufferIndex);
|
|
stream.Flush ();
|
|
|
|
if (!isLastSegment && packetType == TdsPacketType.Bulk)
|
|
{
|
|
System.Threading.Thread.Sleep (100);
|
|
}
|
|
|
|
packetsSent++;
|
|
}
|
|
}
|
|
|
|
public void Skip (long i)
|
|
{
|
|
for ( ; i > 0; i--)
|
|
GetByte ();
|
|
}
|
|
|
|
public void StartPacket (TdsPacketType type)
|
|
{
|
|
if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
|
|
inBufferIndex = inBufferLength;
|
|
|
|
packetType = type;
|
|
nextOutBufferIndex = headerLength;
|
|
}
|
|
|
|
private void Store (int index, byte value)
|
|
{
|
|
outBuffer[index] = value;
|
|
}
|
|
|
|
private void Store (int index, short value)
|
|
{
|
|
outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
|
|
outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
|
|
}
|
|
|
|
#endregion // Methods
|
|
#region Async Methods
|
|
|
|
public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
|
|
{
|
|
TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
|
|
|
|
stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
|
|
return ar;
|
|
}
|
|
|
|
/// <returns>Packet size in bytes</returns>
|
|
public int EndReadPacket (IAsyncResult ar)
|
|
{
|
|
if (!ar.IsCompleted)
|
|
ar.AsyncWaitHandle.WaitOne ();
|
|
return (int) ((TdsAsyncResult) ar).ReturnValue;
|
|
}
|
|
|
|
|
|
public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
|
|
{
|
|
TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
|
|
int nread = stream.EndRead (socketAsyncResult);
|
|
int n;
|
|
|
|
while (nread < 8) {
|
|
n = Read (tmpBuf, nread, 8 - nread);
|
|
if (n <= 0) {
|
|
socket = null;
|
|
stream.Close ();
|
|
throw new IOException (n == 0 ? "Connection lost" : "Connection error");
|
|
}
|
|
nread += n;
|
|
}
|
|
|
|
TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
|
|
if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
|
|
{
|
|
throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
|
|
}
|
|
|
|
// figure out how many bytes are remaining in this packet.
|
|
int len = Ntohs (tmpBuf, 2) - 8;
|
|
|
|
if (len >= inBuffer.Length)
|
|
inBuffer = new byte[len];
|
|
|
|
if (len < 0) {
|
|
throw new Exception (String.Format ("Confused by a length of {0}", len));
|
|
}
|
|
|
|
GetPhysicalPacketData (len);
|
|
int value = len + 8;
|
|
ar.ReturnValue = ((object)value); // packet size
|
|
ar.MarkComplete ();
|
|
}
|
|
|
|
#endregion // Async Methods
|
|
|
|
}
|
|
|
|
}
|