2 // Mono.Data.Tds.Protocol.TdsComm.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Copyright (C) 2002 Tim Coleman
9 // Copyright (c) 2009 Novell, Inc.
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System
.Net
.Sockets
;
38 using System
.Threading
;
40 namespace Mono
.Data
.Tds
.Protocol
{
41 internal sealed class TdsComm
47 TdsPacketType packetType
= TdsPacketType
.None
;
56 int nextOutBufferIndex
= 0;
61 int inBufferIndex
= 0;
63 static int headerLength
= 8;
65 byte[] tmpBuf
= new byte[8];
66 byte[] resBuffer
= new byte[256];
69 int packetsReceived
= 0;
72 TdsVersion tdsVersion
;
78 public TdsComm (string dataSource
, int port
, int packetSize
, int timeout
, TdsVersion tdsVersion
)
80 this.packetSize
= packetSize
;
81 this.tdsVersion
= tdsVersion
;
82 this.dataSource
= dataSource
;
84 outBuffer
= new byte[packetSize
];
85 inBuffer
= new byte[packetSize
];
87 outBufferLength
= packetSize
;
88 inBufferLength
= packetSize
;
93 bool have_exception
= false;
97 if(IPAddress
.TryParse(this.dataSource
, out ip
)) {
98 endPoint
= new IPEndPoint(ip
, port
);
100 IPHostEntry hostEntry
= Dns
.GetHostEntry (this.dataSource
);
101 endPoint
= new IPEndPoint(hostEntry
.AddressList
[0], port
);
103 } catch (SocketException e
) {
104 throw new TdsInternalException ("Server does not exist or connection refused.", e
);
108 socket
= new Socket (endPoint
.AddressFamily
, SocketType
.Stream
, ProtocolType
.Tcp
);
109 IAsyncResult ares
= socket
.BeginConnect (endPoint
, null, null);
110 int timeout_ms
= timeout
* 1000;
111 if (timeout
> 0 && !ares
.IsCompleted
&& !ares
.AsyncWaitHandle
.WaitOne (timeout_ms
, false))
112 throw Tds
.CreateTimeoutException (dataSource
, "Open()");
113 socket
.EndConnect (ares
);
115 // MS sets these socket option
116 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.KeepAlive
, 1);
117 } catch (SocketException
) {
118 // Some platform may throw an exception, so
119 // eat all socket exception, yeaowww!
123 socket
.NoDelay
= true;
124 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.SendTimeout
, timeout_ms
);
125 socket
.SetSocketOption (SocketOptionLevel
.Socket
, SocketOptionName
.ReceiveTimeout
, timeout_ms
);
127 // Ignore exceptions here for systems that do not support these options.
129 // Let the stream own the socket and take the pleasure of closing it
130 stream
= new NetworkStream (socket
, true);
131 } catch (SocketException e
) {
132 have_exception
= true;
133 throw new TdsInternalException ("Server does not exist or connection refused.", e
);
134 } catch (Exception
) {
135 have_exception
= true;
138 if (have_exception
&& socket
!= null) {
146 if (!socket
.Connected
)
147 throw new TdsInternalException ("Server does not exist or connection refused.", null);
151 #endregion // Constructors
155 public int CommandTimeout
{
156 get { return commandTimeout; }
157 set { commandTimeout = value; }
160 internal Encoding Encoder
{
161 get { return encoder; }
162 set { encoder = value; }
165 public int PacketSize
{
166 get { return packetSize; }
167 set { packetSize = value; }
170 public bool TdsByteOrder
{
172 set { lsb = !value; }
174 #endregion // Properties
178 public byte[] Swap(byte[] toswap
) {
179 byte[] ret
= new byte[toswap
.Length
];
180 for(int i
= 0; i
< toswap
.Length
; i
++)
181 ret
[toswap
.Length
- i
- 1] = toswap
[i
];
186 public void SendIfFull ()
188 if (nextOutBufferIndex
== outBufferLength
) {
189 SendPhysicalPacket (false);
190 nextOutBufferIndex
= headerLength
;
194 public void SendIfFull (int reserve
)
196 if (nextOutBufferIndex
+reserve
> outBufferLength
) {
197 SendPhysicalPacket (false);
198 nextOutBufferIndex
= headerLength
;
202 public void Append (object o
)
204 if (o
== null || o
== DBNull
.Value
) {
209 switch (Type
.GetTypeCode (o
.GetType ())) {
213 case TypeCode
.Boolean
:
219 case TypeCode
.Object
:
223 Append (((Guid
) o
).ToByteArray ());
226 case TypeCode
.Int16
:
229 case TypeCode
.Int32
:
232 case TypeCode
.String
:
235 case TypeCode
.Double
:
238 case TypeCode
.Single
:
241 case TypeCode
.Int64
:
244 case TypeCode
.Decimal
:
245 Append ((decimal) o
, 17);
247 case TypeCode
.DateTime
:
248 Append ((DateTime
) o
, 8);
251 throw new InvalidOperationException (String
.Format ("Object Type :{0} , not being appended", o
.GetType ()));
254 public void Append (byte b
)
257 Store (nextOutBufferIndex
, b
);
258 nextOutBufferIndex
++;
261 public void Append (DateTime t
, int bytes
)
263 DateTime epoch
= new DateTime (1900,1,1);
265 TimeSpan span
= t
- epoch
; //new TimeSpan (t.Ticks - epoch.Ticks);
266 int days
, hours
, minutes
, secs
;
272 minutes
= span
.Minutes
;
274 msecs
= span
.Milliseconds
;
277 // If t.Hour/Min/Sec/MSec is > 0, days points to the next day and hence,
278 // we move it back by a day - otherwise, no change
279 days
= (t
.Hour
> 0 || t
.Minute
> 0 || t
.Second
> 0 || t
.Millisecond
> 0) ? days
-1: days
;
283 msecs
= t
.Millisecond
;
288 long ms
= (hours
* 3600 + minutes
* 60 + secs
)*1000L + (long)msecs
;
289 val
= (int) ((ms
*300)/1000);
290 AppendInternal ((int) days
);
291 AppendInternal ((int) val
);
292 } else if (bytes
==4) {
293 val
= span
.Hours
* 60 + span
.Minutes
;
294 AppendInternal ((short) days
);
295 AppendInternal ((short) val
);
297 throw new Exception ("Invalid No of bytes");
301 public void Append (byte[] b
)
303 Append (b
, b
.Length
, (byte) 0);
307 public void Append (byte[] b
, int len
, byte pad
)
309 int bufBytesToCopy
= System
.Math
.Min (b
.Length
, len
);
310 int padBytesToCopy
= len
- bufBytesToCopy
;
313 /* copy out of our input buffer in the largest chunks possible *
314 * at a time. limited only by the buffer size for our outgoing *
317 while (bufBytesToCopy
> 0)
321 int availBytes
= outBufferLength
- nextOutBufferIndex
;
322 int bufSize
= System
.Math
.Min (availBytes
, bufBytesToCopy
);
324 Buffer
.BlockCopy (b
, bufPos
, outBuffer
, nextOutBufferIndex
, bufSize
);
326 nextOutBufferIndex
+= bufSize
;
327 bufBytesToCopy
-= bufSize
;
331 while (padBytesToCopy
> 0)
335 int availBytes
= outBufferLength
- nextOutBufferIndex
;
336 int bufSize
= System
.Math
.Min (availBytes
, padBytesToCopy
);
338 for (int i
= 0; i
< bufSize
; i
++)
339 outBuffer
[nextOutBufferIndex
++] = pad
;
341 padBytesToCopy
-= bufSize
;
345 private void AppendInternal (short s
)
348 outBuffer
[nextOutBufferIndex
++] = (byte) (((byte) (s
>> 8)) & 0xff);
349 outBuffer
[nextOutBufferIndex
++] = (byte) ((byte) (s
& 0xff));
351 outBuffer
[nextOutBufferIndex
++] = (byte) ((byte) (s
& 0xff));
352 outBuffer
[nextOutBufferIndex
++] = (byte) (((byte) (s
>> 8)) & 0xff);
356 public void Append (short s
)
358 SendIfFull (sizeof (short));
362 public void Append (ushort s
)
364 SendIfFull (sizeof (short));
365 AppendInternal ((short) s
);
368 private void AppendInternal (int i
)
371 AppendInternal ((short) (((short) (i
>> 16)) & 0xffff));
372 AppendInternal ((short) ((short) (i
& 0xffff)));
374 AppendInternal ((short) ((short) (i
& 0xffff)));
375 AppendInternal ((short) (((short) (i
>> 16)) & 0xffff));
379 public void Append (int i
)
381 SendIfFull (sizeof (int));
385 public void Append (string s
)
387 if (tdsVersion
< TdsVersion
.tds70
) {
388 Append (encoder
.GetBytes (s
));
390 for (int i
= 0; i
< s
.Length
; i
++) {
391 SendIfFull (sizeof(short));
392 AppendInternal ((short)s
[i
]);
397 public void AppendNonUnicode (string s
)
399 if (tdsVersion
< TdsVersion
.tds70
) {
400 Append (encoder
.GetBytes (s
));
402 for (int i
= 0; i
< s
.Length
; i
++) {
403 SendIfFull (sizeof(byte));
409 // Appends with padding
410 public byte[] Append (string s
, int len
, byte pad
)
415 byte[] result
= encoder
.GetBytes (s
);
416 Append (result
, len
, pad
);
420 public void Append (double value)
423 Append (Swap (BitConverter
.GetBytes (value)), sizeof(double), (byte)0);
425 Append (BitConverter
.GetBytes (value), sizeof(double), (byte)0);
428 public void Append (float value)
431 Append (Swap (BitConverter
.GetBytes (value)), sizeof(float), (byte)0);
433 Append (BitConverter
.GetBytes (value), sizeof(float), (byte)0);
436 public void Append (long l
)
438 SendIfFull (sizeof (long));
440 AppendInternal ((int) (((int) (l
>> 32)) & 0xffffffff));
441 AppendInternal ((int) ((int) (l
& 0xffffffff)));
443 AppendInternal ((int) ((int) (l
& 0xffffffff)));
444 AppendInternal ((int) (((int) (l
>> 32)) & 0xffffffff));
448 public void Append (decimal d
, int bytes
)
450 int[] arr
= Decimal
.GetBits (d
);
451 byte sign
= (d
> 0 ? (byte)1 : (byte)0);
454 AppendInternal (arr
[0]);
455 AppendInternal (arr
[1]);
456 AppendInternal (arr
[2]);
457 AppendInternal ((int)0);
460 public void AppendMoney (decimal d
, int size
)
462 // The method for this is to simply multiply by 10^4 and then stuff
463 // the value into either a int or long value depending on the size
467 decimal tmpD
= Decimal
.Multiply(d
, 10000m
);
469 long longValue
= Decimal
.ToInt64(tmpD
);
471 int significantHalf
= (int) ((longValue
>> 32) & 0xffffffff);
472 int lessSignificantHalf
= (int)(longValue
& 0xffffffff);
474 AppendInternal (significantHalf
);
475 AppendInternal (lessSignificantHalf
);
477 int intValue
= Decimal
.ToInt32(tmpD
);
478 AppendInternal (intValue
);
482 // A method for decimals that properly scales the decimal out before putting on the TDS steam
483 public void AppendDecimal (decimal d
, int bytes
, int scale
)
485 decimal tmpD1
= Decimal
.Multiply (d
, (decimal)System
.Math
.Pow (10.0, scale
));
486 decimal tmpD2
= System
.Math
.Abs(Decimal
.Truncate (tmpD1
));
488 int[] arr
= Decimal
.GetBits (tmpD2
);
489 byte sign
= (d
> 0 ? (byte)1 : (byte)0);
492 AppendInternal (arr
[0]);
493 AppendInternal (arr
[1]);
494 AppendInternal (arr
[2]);
495 AppendInternal ((int)0);
512 public bool IsConnected ()
514 return socket
!= null && socket
.Connected
&& !(socket
.Poll (0, SelectMode
.SelectRead
) && socket
.Available
== 0);
517 public byte GetByte ()
521 if (inBufferIndex
>= inBufferLength
) {
522 // out of data, read another physical packet.
523 GetPhysicalPacket ();
525 result
= inBuffer
[inBufferIndex
++];
529 public byte[] GetBytes (int len
, bool exclusiveBuffer
)
531 byte[] result
= null;
534 // Do not keep an internal result buffer larger than 16k.
535 // This would unnecessarily use up memory.
536 if (exclusiveBuffer
|| len
> 16384)
537 result
= new byte[len
];
540 if (resBuffer
.Length
< len
)
541 resBuffer
= new byte[len
];
547 if (inBufferIndex
>= inBufferLength
)
548 GetPhysicalPacket ();
550 int avail
= inBufferLength
- inBufferIndex
;
551 avail
= avail
>len
-i
? len
-i
: avail
;
553 Buffer
.BlockCopy (inBuffer
, inBufferIndex
, result
, i
, avail
);
555 inBufferIndex
+= avail
;
561 public string GetString (int len
, Encoding enc
)
563 if (tdsVersion
>= TdsVersion
.tds70
)
564 return GetString (len
, true, null);
566 return GetString (len
, false, null);
569 public string GetString (int len
)
571 if (tdsVersion
>= TdsVersion
.tds70
)
572 return GetString (len
, true);
574 return GetString (len
, false);
577 public string GetString (int len
, bool wide
, Encoding enc
)
580 char[] chars
= new char[len
];
581 for (int i
= 0; i
< len
; ++i
) {
582 int lo
= ((byte) GetByte ()) & 0xFF;
583 int hi
= ((byte) GetByte ()) & 0xFF;
584 chars
[i
] = (char) (lo
| ( hi
<< 8));
586 return new String (chars
);
589 byte[] result
= new byte[len
];
590 Array
.Copy (GetBytes (len
, false), result
, len
);
591 // Use the passed encoder, if available
593 return (enc
.GetString (result
));
595 return (encoder
.GetString (result
));
599 public string GetString (int len
, bool wide
)
601 return GetString (len
, wide
, null);
604 public int GetNetShort ()
606 byte[] tmp
= new byte[2];
609 return Ntohs (tmp
, 0);
612 public short GetTdsShort ()
614 byte[] input
= new byte[2];
616 for (int i
= 0; i
< 2; i
+= 1)
617 input
[i
] = GetByte ();
618 if(!BitConverter
.IsLittleEndian
)
619 return (BitConverter
.ToInt16 (Swap (input
), 0));
621 return (BitConverter
.ToInt16 (input
, 0));
625 public int GetTdsInt ()
627 byte[] input
= new byte[4];
628 for (int i
= 0; i
< 4; i
+= 1) {
629 input
[i
] = GetByte ();
631 if(!BitConverter
.IsLittleEndian
)
632 return (BitConverter
.ToInt32 (Swap (input
), 0));
634 return (BitConverter
.ToInt32 (input
, 0));
637 public long GetTdsInt64 ()
639 byte[] input
= new byte[8];
640 for (int i
= 0; i
< 8; i
+= 1)
641 input
[i
] = GetByte ();
642 if(!BitConverter
.IsLittleEndian
)
643 return (BitConverter
.ToInt64 (Swap (input
), 0));
645 return (BitConverter
.ToInt64 (input
, 0));
648 private void GetPhysicalPacket ()
650 int dataLength
= GetPhysicalPacketHeader ();
651 GetPhysicalPacketData (dataLength
);
654 int Read (byte [] buffer
, int offset
, int count
)
657 return stream
.Read (buffer
, offset
, count
);
665 private int GetPhysicalPacketHeader ()
672 n
= Read (tmpBuf
, nread
, 8 - nread
);
676 throw new IOException (n
== 0 ? "Connection lost" : "Connection error");
681 TdsPacketType packetType
= (TdsPacketType
) tmpBuf
[0];
682 if (packetType
!= TdsPacketType
.Logon
&& packetType
!= TdsPacketType
.Query
&& packetType
!= TdsPacketType
.Reply
)
684 throw new Exception (String
.Format ("Unknown packet type {0}", tmpBuf
[0]));
687 // figure out how many bytes are remaining in this packet.
688 int len
= Ntohs (tmpBuf
, 2) - 8;
689 if (len
>= inBuffer
.Length
)
690 inBuffer
= new byte[len
];
693 throw new Exception (String
.Format ("Confused by a length of {0}", len
));
700 private void GetPhysicalPacketData (int length
)
706 while (nread
< length
) {
707 n
= Read (inBuffer
, nread
, length
- nread
);
711 throw new IOException (n
== 0 ? "Connection lost" : "Connection error");
718 // adjust the bookkeeping info about the incoming buffer
719 inBufferLength
= length
;
724 private static int Ntohs (byte[] buf
, int offset
)
726 int lo
= ((int) buf
[offset
+ 1] & 0xff);
727 int hi
= (((int) buf
[offset
] & 0xff ) << 8);
730 // return an int since we really want an _unsigned_
735 // If out of data, read another physical packet.
736 if (inBufferIndex
>= inBufferLength
)
737 GetPhysicalPacket ();
739 return inBuffer
[inBufferIndex
];
742 public bool Poll (int seconds
, SelectMode selectMode
)
744 return Poll (socket
, seconds
, selectMode
);
747 private bool Poll (Socket s
, int seconds
, SelectMode selectMode
)
749 long uSeconds
= seconds
* 1000000;
752 while (uSeconds
> (long) Int32
.MaxValue
) {
753 bState
= s
.Poll (Int32
.MaxValue
, selectMode
);
756 uSeconds
-= Int32
.MaxValue
;
758 return s
.Poll ((int) uSeconds
, selectMode
);
761 internal void ResizeOutBuf (int newSize
)
763 if (newSize
!= outBufferLength
) {
764 byte[] newBuf
= new byte [newSize
];
765 Buffer
.BlockCopy (outBuffer
, 0, newBuf
, 0, newSize
);
766 outBufferLength
= newSize
;
771 public bool ResetConnection
{
772 get { return connReset; }
773 set { connReset = value; }
776 public void SendPacket ()
778 // Reset connection flag is only valid for SQLBatch/RPC/DTC messages
779 if (packetType
!= TdsPacketType
.Query
&& packetType
!= TdsPacketType
.RPC
)
782 SendPhysicalPacket (true);
783 nextOutBufferIndex
= 0;
784 packetType
= TdsPacketType
.None
;
785 // Reset connection-reset flag to false - as any exception would anyway close
786 // the whole connection
791 private void SendPhysicalPacket (bool isLastSegment
)
793 if (nextOutBufferIndex
> headerLength
|| packetType
== TdsPacketType
.Cancel
) {
794 byte status
= (byte) ((isLastSegment
? 0x01 : 0x00) | (connReset
? 0x08 : 0x00));
797 Store (0, (byte) packetType
);
799 Store (2, (short) nextOutBufferIndex
);
802 if (tdsVersion
>= TdsVersion
.tds70
)
803 Store (6, (byte) packetsSent
);
808 stream
.Write (outBuffer
, 0, nextOutBufferIndex
);
811 if (!isLastSegment
&& packetType
== TdsPacketType
.Bulk
)
813 System
.Threading
.Thread
.Sleep (100);
820 public void Skip (long i
)
826 public void StartPacket (TdsPacketType type
)
828 if (type
!= TdsPacketType
.Cancel
&& inBufferIndex
!= inBufferLength
)
829 inBufferIndex
= inBufferLength
;
832 nextOutBufferIndex
= headerLength
;
835 private void Store (int index
, byte value)
837 outBuffer
[index
] = value;
840 private void Store (int index
, short value)
842 outBuffer
[index
] = (byte) (((byte) (value >> 8)) & 0xff);
843 outBuffer
[index
+ 1] = (byte) (((byte) (value >> 0)) & 0xff);
846 #endregion // Methods
847 #region Async Methods
849 public IAsyncResult
BeginReadPacket (AsyncCallback callback
, object stateObject
)
851 TdsAsyncResult ar
= new TdsAsyncResult (callback
, stateObject
);
853 stream
.BeginRead (tmpBuf
, 0, 8, new AsyncCallback(OnReadPacketCallback
), ar
);
857 /// <returns>Packet size in bytes</returns>
858 public int EndReadPacket (IAsyncResult ar
)
861 ar
.AsyncWaitHandle
.WaitOne ();
862 return (int) ((TdsAsyncResult
) ar
).ReturnValue
;
866 public void OnReadPacketCallback (IAsyncResult socketAsyncResult
)
868 TdsAsyncResult ar
= (TdsAsyncResult
) socketAsyncResult
.AsyncState
;
869 int nread
= stream
.EndRead (socketAsyncResult
);
873 n
= Read (tmpBuf
, nread
, 8 - nread
);
877 throw new IOException (n
== 0 ? "Connection lost" : "Connection error");
882 TdsPacketType packetType
= (TdsPacketType
) tmpBuf
[0];
883 if (packetType
!= TdsPacketType
.Logon
&& packetType
!= TdsPacketType
.Query
&& packetType
!= TdsPacketType
.Reply
)
885 throw new Exception (String
.Format ("Unknown packet type {0}", tmpBuf
[0]));
888 // figure out how many bytes are remaining in this packet.
889 int len
= Ntohs (tmpBuf
, 2) - 8;
891 if (len
>= inBuffer
.Length
)
892 inBuffer
= new byte[len
];
895 throw new Exception (String
.Format ("Confused by a length of {0}", len
));
898 GetPhysicalPacketData (len
);
900 ar
.ReturnValue
= ((object)value); // packet size
904 #endregion // Async Methods