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;
98 if(IPAddress.TryParse(this.dataSource, out ip)) {
99 endPoint = new IPEndPoint(ip, port);
101 IPHostEntry hostEntry = Dns.GetHostEntry (this.dataSource);
102 endPoint = new IPEndPoint(hostEntry.AddressList [0], port);
105 IPHostEntry hostEntry = Dns.Resolve (this.dataSource);
106 endPoint = new IPEndPoint (hostEntry.AddressList [0], port);
108 } catch (SocketException e) {
109 throw new TdsInternalException ("Server does not exist or connection refused.", e);
113 socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
114 IAsyncResult ares = socket.BeginConnect (endPoint, null, null);
115 int timeout_ms = timeout * 1000;
116 if (timeout > 0 && !ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (timeout_ms, false))
117 throw Tds.CreateTimeoutException (dataSource, "Open()");
118 socket.EndConnect (ares);
120 // MS sets these socket option
121 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
122 } catch (SocketException) {
123 // Some platform may throw an exception, so
124 // eat all socket exception, yeaowww!
129 socket.NoDelay = true;
131 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout_ms);
132 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout_ms);
134 // Ignore exceptions here for systems that do not support these options.
136 // Let the stream own the socket and take the pleasure of closing it
137 stream = new NetworkStream (socket, true);
138 } catch (SocketException e) {
139 have_exception = true;
140 throw new TdsInternalException ("Server does not exist or connection refused.", e);
141 } catch (Exception) {
142 have_exception = true;
145 if (have_exception && socket != null) {
153 if (!socket.Connected)
154 throw new TdsInternalException ("Server does not exist or connection refused.", null);
158 #endregion // Constructors
162 public int CommandTimeout {
163 get { return commandTimeout; }
164 set { commandTimeout = value; }
167 internal Encoding Encoder {
168 get { return encoder; }
169 set { encoder = value; }
172 public int PacketSize {
173 get { return packetSize; }
174 set { packetSize = value; }
177 public bool TdsByteOrder {
179 set { lsb = !value; }
181 #endregion // Properties
185 public byte[] Swap(byte[] toswap) {
186 byte[] ret = new byte[toswap.Length];
187 for(int i = 0; i < toswap.Length; i++)
188 ret [toswap.Length - i - 1] = toswap[i];
193 public void SendIfFull ()
195 if (nextOutBufferIndex == outBufferLength) {
196 SendPhysicalPacket (false);
197 nextOutBufferIndex = headerLength;
201 public void SendIfFull (int reserve)
203 if (nextOutBufferIndex+reserve > outBufferLength) {
204 SendPhysicalPacket (false);
205 nextOutBufferIndex = headerLength;
209 public void Append (object o)
211 if (o == null || o == DBNull.Value) {
216 switch (Type.GetTypeCode (o.GetType ())) {
220 case TypeCode.Boolean:
226 case TypeCode.Object :
230 case TypeCode.Int16 :
233 case TypeCode.Int32 :
236 case TypeCode.String :
239 case TypeCode.Double :
242 case TypeCode.Single :
245 case TypeCode.Int64 :
248 case TypeCode.Decimal:
249 Append ((decimal) o, 17);
251 case TypeCode.DateTime:
252 Append ((DateTime) o, 8);
255 throw new InvalidOperationException (String.Format ("Object Type :{0} , not being appended", o.GetType ()));
258 public void Append (byte b)
261 Store (nextOutBufferIndex, b);
262 nextOutBufferIndex++;
265 public void Append (DateTime t, int bytes)
267 DateTime epoch = new DateTime (1900,1,1);
269 TimeSpan span = t - epoch; //new TimeSpan (t.Ticks - epoch.Ticks);
270 int days, hours, minutes, secs;
276 minutes = span.Minutes;
278 msecs = span.Milliseconds;
281 // If t.Hour/Min/Sec/MSec is > 0, days points to the next day and hence,
282 // we move it back by a day - otherwise, no change
283 days = (t.Hour > 0 || t.Minute > 0 || t.Second > 0 || t.Millisecond > 0) ? days-1: days;
287 msecs = t.Millisecond;
292 long ms = (hours * 3600 + minutes * 60 + secs)*1000L + (long)msecs;
293 val = (int) ((ms*300)/1000);
294 AppendInternal ((int) days);
295 AppendInternal ((int) val);
296 } else if (bytes ==4) {
297 val = span.Hours * 60 + span.Minutes;
298 AppendInternal ((short) days);
299 AppendInternal ((short) val);
301 throw new Exception ("Invalid No of bytes");
305 public void Append (byte[] b)
307 Append (b, b.Length, (byte) 0);
311 public void Append (byte[] b, int len, byte pad)
313 int bufBytesToCopy = System.Math.Min (b.Length, len);
314 int padBytesToCopy = len - bufBytesToCopy;
317 /* copy out of our input buffer in the largest chunks possible *
318 * at a time. limited only by the buffer size for our outgoing *
321 while (bufBytesToCopy > 0)
325 int availBytes = outBufferLength - nextOutBufferIndex;
326 int bufSize = System.Math.Min (availBytes, bufBytesToCopy);
328 Buffer.BlockCopy (b, bufPos, outBuffer, nextOutBufferIndex, bufSize);
330 nextOutBufferIndex += bufSize;
331 bufBytesToCopy -= bufSize;
335 while (padBytesToCopy > 0)
339 int availBytes = outBufferLength - nextOutBufferIndex;
340 int bufSize = System.Math.Min (availBytes, padBytesToCopy);
342 for (int i = 0; i < bufSize; i++)
343 outBuffer [nextOutBufferIndex++] = pad;
345 padBytesToCopy -= bufSize;
349 private void AppendInternal (short s)
352 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
353 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
355 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
356 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
360 public void Append (short s)
362 SendIfFull (sizeof (short));
366 public void Append (ushort s)
368 SendIfFull (sizeof (short));
369 AppendInternal ((short) s);
372 private void AppendInternal (int i)
375 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
376 AppendInternal ((short) ((short) (i & 0xffff)));
378 AppendInternal ((short) ((short) (i & 0xffff)));
379 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
383 public void Append (int i)
385 SendIfFull (sizeof (int));
389 public void Append (string s)
391 if (tdsVersion < TdsVersion.tds70) {
392 Append (encoder.GetBytes (s));
394 int cindex = 0, index;
395 int ssize = sizeof (short);
396 int lenToWrite = s.Length * ssize;
397 // if nextOutBufferLength points to the last buffer in outBuffer,
398 // we would get a DivisionByZero while calculating remBufLen
399 if (outBufferLength - nextOutBufferIndex < ssize)
402 int remBufLen = outBufferLength - nextOutBufferIndex;
403 int count = lenToWrite/remBufLen;
405 if (lenToWrite % remBufLen > 0)
408 for (int i = 0; i < count; i++) {
409 index = System.Math.Min (remBufLen/ssize, lenToWrite/ssize);
410 for (int j = 0; j < index*ssize; j+=2, cindex++)
411 AppendInternal ((short)s[cindex]);
413 lenToWrite -= index*ssize;
414 // Just make sure to flush the buffer
415 SendIfFull ((lenToWrite+1)*ssize);
420 // Appends with padding
421 public byte[] Append (string s, int len, byte pad)
426 byte[] result = encoder.GetBytes (s);
427 Append (result, len, pad);
431 public void Append (double value)
434 Append (Swap (BitConverter.GetBytes (value)), sizeof(double), (byte)0);
436 Append (BitConverter.GetBytes (value), sizeof(double), (byte)0);
439 public void Append (float value)
442 Append (Swap (BitConverter.GetBytes (value)), sizeof(float), (byte)0);
444 Append (BitConverter.GetBytes (value), sizeof(float), (byte)0);
447 public void Append (long l)
449 SendIfFull (sizeof (long));
451 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
452 AppendInternal ((int) ((int) (l & 0xffffffff)));
454 AppendInternal ((int) ((int) (l & 0xffffffff)));
455 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
459 public void Append (decimal d, int bytes)
461 int[] arr = Decimal.GetBits (d);
462 byte sign = (d > 0 ? (byte)1 : (byte)0);
465 AppendInternal (arr[0]);
466 AppendInternal (arr[1]);
467 AppendInternal (arr[2]);
468 AppendInternal ((int)0);
485 public bool IsConnected ()
487 return socket != null && socket.Connected && !(socket.Poll (0, SelectMode.SelectRead) && socket.Available == 0);
490 public byte GetByte ()
494 if (inBufferIndex >= inBufferLength) {
495 // out of data, read another physical packet.
496 GetPhysicalPacket ();
498 result = inBuffer[inBufferIndex++];
502 public byte[] GetBytes (int len, bool exclusiveBuffer)
504 byte[] result = null;
507 // Do not keep an internal result buffer larger than 16k.
508 // This would unnecessarily use up memory.
509 if (exclusiveBuffer || len > 16384)
510 result = new byte[len];
513 if (resBuffer.Length < len)
514 resBuffer = new byte[len];
520 if (inBufferIndex >= inBufferLength)
521 GetPhysicalPacket ();
523 int avail = inBufferLength - inBufferIndex;
524 avail = avail>len-i ? len-i : avail;
526 Buffer.BlockCopy (inBuffer, inBufferIndex, result, i, avail);
528 inBufferIndex += avail;
534 public string GetString (int len, Encoding enc)
536 if (tdsVersion >= TdsVersion.tds70)
537 return GetString (len, true, null);
539 return GetString (len, false, null);
542 public string GetString (int len)
544 if (tdsVersion >= TdsVersion.tds70)
545 return GetString (len, true);
547 return GetString (len, false);
550 public string GetString (int len, bool wide, Encoding enc)
553 char[] chars = new char[len];
554 for (int i = 0; i < len; ++i) {
555 int lo = ((byte) GetByte ()) & 0xFF;
556 int hi = ((byte) GetByte ()) & 0xFF;
557 chars[i] = (char) (lo | ( hi << 8));
559 return new String (chars);
562 byte[] result = new byte[len];
563 Array.Copy (GetBytes (len, false), result, len);
564 // Use the passed encoder, if available
566 return (enc.GetString (result));
568 return (encoder.GetString (result));
572 public string GetString (int len, bool wide)
574 return GetString (len, wide, null);
577 public int GetNetShort ()
579 byte[] tmp = new byte[2];
582 return Ntohs (tmp, 0);
585 public short GetTdsShort ()
587 byte[] input = new byte[2];
589 for (int i = 0; i < 2; i += 1)
590 input[i] = GetByte ();
591 if(!BitConverter.IsLittleEndian)
592 return (BitConverter.ToInt16 (Swap (input), 0));
594 return (BitConverter.ToInt16 (input, 0));
598 public int GetTdsInt ()
600 byte[] input = new byte[4];
601 for (int i = 0; i < 4; i += 1) {
602 input[i] = GetByte ();
604 if(!BitConverter.IsLittleEndian)
605 return (BitConverter.ToInt32 (Swap (input), 0));
607 return (BitConverter.ToInt32 (input, 0));
610 public long GetTdsInt64 ()
612 byte[] input = new byte[8];
613 for (int i = 0; i < 8; i += 1)
614 input[i] = GetByte ();
615 if(!BitConverter.IsLittleEndian)
616 return (BitConverter.ToInt64 (Swap (input), 0));
618 return (BitConverter.ToInt64 (input, 0));
621 private void GetPhysicalPacket ()
623 int dataLength = GetPhysicalPacketHeader ();
624 GetPhysicalPacketData (dataLength);
627 int Read (byte [] buffer, int offset, int count)
630 return stream.Read (buffer, offset, count);
638 private int GetPhysicalPacketHeader ()
645 n = Read (tmpBuf, nread, 8 - nread);
649 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
654 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
655 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
657 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
660 // figure out how many bytes are remaining in this packet.
661 int len = Ntohs (tmpBuf, 2) - 8;
662 if (len >= inBuffer.Length)
663 inBuffer = new byte[len];
666 throw new Exception (String.Format ("Confused by a length of {0}", len));
673 private void GetPhysicalPacketData (int length)
679 while (nread < length) {
680 n = Read (inBuffer, nread, length - nread);
684 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
691 // adjust the bookkeeping info about the incoming buffer
692 inBufferLength = length;
697 private static int Ntohs (byte[] buf, int offset)
699 int lo = ((int) buf[offset + 1] & 0xff);
700 int hi = (((int) buf[offset] & 0xff ) << 8);
703 // return an int since we really want an _unsigned_
708 // If out of data, read another physical packet.
709 if (inBufferIndex >= inBufferLength)
710 GetPhysicalPacket ();
712 return inBuffer[inBufferIndex];
715 public bool Poll (int seconds, SelectMode selectMode)
717 return Poll (socket, seconds, selectMode);
720 private bool Poll (Socket s, int seconds, SelectMode selectMode)
722 long uSeconds = seconds * 1000000;
725 while (uSeconds > (long) Int32.MaxValue) {
726 bState = s.Poll (Int32.MaxValue, selectMode);
729 uSeconds -= Int32.MaxValue;
731 return s.Poll ((int) uSeconds, selectMode);
734 internal void ResizeOutBuf (int newSize)
736 if (newSize != outBufferLength) {
737 byte[] newBuf = new byte [newSize];
738 Buffer.BlockCopy (outBuffer, 0, newBuf, 0, newSize);
739 outBufferLength = newSize;
744 public bool ResetConnection {
745 get { return connReset; }
746 set { connReset = value; }
749 public void SendPacket ()
751 // Reset connection flag is only valid for SQLBatch/RPC/DTC messages
752 if (packetType != TdsPacketType.Query && packetType != TdsPacketType.RPC)
755 SendPhysicalPacket (true);
756 nextOutBufferIndex = 0;
757 packetType = TdsPacketType.None;
758 // Reset connection-reset flag to false - as any exception would anyway close
759 // the whole connection
764 private void SendPhysicalPacket (bool isLastSegment)
766 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
767 byte status = (byte) ((isLastSegment ? 0x01 : 0x00) | (connReset ? 0x08 : 0x00));
769 Store (0, (byte) packetType);
771 Store (2, (short) nextOutBufferIndex );
774 if (tdsVersion >= TdsVersion.tds70)
775 Store (6, (byte) packetsSent);
780 stream.Write (outBuffer, 0, nextOutBufferIndex);
786 public void Skip (long i)
792 public void StartPacket (TdsPacketType type)
794 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
795 inBufferIndex = inBufferLength;
798 nextOutBufferIndex = headerLength;
801 private void Store (int index, byte value)
803 outBuffer[index] = value;
806 private void Store (int index, short value)
808 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
809 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
812 #endregion // Methods
814 #region Async Methods
816 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
818 TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
820 stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
824 /// <returns>Packet size in bytes</returns>
825 public int EndReadPacket (IAsyncResult ar)
828 ar.AsyncWaitHandle.WaitOne ();
829 return (int) ((TdsAsyncResult) ar).ReturnValue;
833 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
835 TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
836 int nread = stream.EndRead (socketAsyncResult);
840 n = Read (tmpBuf, nread, 8 - nread);
844 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
849 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
850 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
852 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
855 // figure out how many bytes are remaining in this packet.
856 int len = Ntohs (tmpBuf, 2) - 8;
858 if (len >= inBuffer.Length)
859 inBuffer = new byte[len];
862 throw new Exception (String.Format ("Confused by a length of {0}", len));
865 GetPhysicalPacketData (len);
867 ar.ReturnValue = ((object)value); // packet size
871 #endregion // Async Methods