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 is > 0, days points to the next day and hence,
282 // we move it back by a day - otherwise, no change
292 msecs = t.Millisecond;
297 long ms = (hours * 3600 + minutes * 60 + secs)*1000L + (long)msecs;
298 val = (int) ((ms*300)/1000);
299 AppendInternal ((int) days);
300 AppendInternal ((int) val);
301 } else if (bytes ==4) {
302 val = span.Hours * 60 + span.Minutes;
303 AppendInternal ((short) days);
304 AppendInternal ((short) val);
306 throw new Exception ("Invalid No of bytes");
310 public void Append (byte[] b)
312 Append (b, b.Length, (byte) 0);
316 public void Append (byte[] b, int len, byte pad)
318 int bufBytesToCopy = System.Math.Min (b.Length, len);
319 int padBytesToCopy = len - bufBytesToCopy;
322 /* copy out of our input buffer in the largest chunks possible *
323 * at a time. limited only by the buffer size for our outgoing *
326 while (bufBytesToCopy > 0)
330 int availBytes = outBufferLength - nextOutBufferIndex;
331 int bufSize = System.Math.Min (availBytes, bufBytesToCopy);
333 Buffer.BlockCopy (b, bufPos, outBuffer, nextOutBufferIndex, bufSize);
335 nextOutBufferIndex += bufSize;
336 bufBytesToCopy -= bufSize;
340 while (padBytesToCopy > 0)
344 int availBytes = outBufferLength - nextOutBufferIndex;
345 int bufSize = System.Math.Min (availBytes, padBytesToCopy);
347 for (int i = 0; i < bufSize; i++)
348 outBuffer [nextOutBufferIndex++] = pad;
350 padBytesToCopy -= bufSize;
354 private void AppendInternal (short s)
357 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
358 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
360 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
361 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
365 public void Append (short s)
367 SendIfFull (sizeof (short));
371 public void Append (ushort s)
373 SendIfFull (sizeof (short));
374 AppendInternal ((short) s);
377 private void AppendInternal (int i)
380 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
381 AppendInternal ((short) ((short) (i & 0xffff)));
383 AppendInternal ((short) ((short) (i & 0xffff)));
384 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
388 public void Append (int i)
390 SendIfFull (sizeof (int));
394 public void Append (string s)
396 if (tdsVersion < TdsVersion.tds70) {
397 Append (encoder.GetBytes (s));
399 int cindex = 0, index;
400 int ssize = sizeof (short);
401 int lenToWrite = s.Length * ssize;
402 // if nextOutBufferLength points to the last buffer in outBuffer,
403 // we would get a DivisionByZero while calculating remBufLen
404 if (outBufferLength - nextOutBufferIndex < ssize)
407 int remBufLen = outBufferLength - nextOutBufferIndex;
408 int count = lenToWrite/remBufLen;
410 if (lenToWrite % remBufLen > 0)
413 for (int i = 0; i < count; i++) {
414 index = System.Math.Min (remBufLen/ssize, lenToWrite/ssize);
415 for (int j = 0; j < index*ssize; j+=2, cindex++)
416 AppendInternal ((short)s[cindex]);
418 lenToWrite -= index*ssize;
419 // Just make sure to flush the buffer
420 SendIfFull ((lenToWrite+1)*ssize);
425 // Appends with padding
426 public byte[] Append (string s, int len, byte pad)
431 byte[] result = encoder.GetBytes (s);
432 Append (result, len, pad);
436 public void Append (double value)
439 Append (Swap (BitConverter.GetBytes (value)), sizeof(double), (byte)0);
441 Append (BitConverter.GetBytes (value), sizeof(double), (byte)0);
444 public void Append (float value)
447 Append (Swap (BitConverter.GetBytes (value)), sizeof(float), (byte)0);
449 Append (BitConverter.GetBytes (value), sizeof(float), (byte)0);
452 public void Append (long l)
454 SendIfFull (sizeof (long));
456 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
457 AppendInternal ((int) ((int) (l & 0xffffffff)));
459 AppendInternal ((int) ((int) (l & 0xffffffff)));
460 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
464 public void Append (decimal d, int bytes)
466 int[] arr = Decimal.GetBits (d);
467 byte sign = (d > 0 ? (byte)1 : (byte)0);
470 AppendInternal (arr[0]);
471 AppendInternal (arr[1]);
472 AppendInternal (arr[2]);
473 AppendInternal ((int)0);
490 public bool IsConnected ()
492 return socket != null && socket.Connected && !(socket.Poll (0, SelectMode.SelectRead) && socket.Available == 0);
495 public byte GetByte ()
499 if (inBufferIndex >= inBufferLength) {
500 // out of data, read another physical packet.
501 GetPhysicalPacket ();
503 result = inBuffer[inBufferIndex++];
507 public byte[] GetBytes (int len, bool exclusiveBuffer)
509 byte[] result = null;
512 // Do not keep an internal result buffer larger than 16k.
513 // This would unnecessarily use up memory.
514 if (exclusiveBuffer || len > 16384)
515 result = new byte[len];
518 if (resBuffer.Length < len)
519 resBuffer = new byte[len];
525 if (inBufferIndex >= inBufferLength)
526 GetPhysicalPacket ();
528 int avail = inBufferLength - inBufferIndex;
529 avail = avail>len-i ? len-i : avail;
531 Buffer.BlockCopy (inBuffer, inBufferIndex, result, i, avail);
533 inBufferIndex += avail;
539 public string GetString (int len, Encoding enc)
541 if (tdsVersion >= TdsVersion.tds70)
542 return GetString (len, true, null);
544 return GetString (len, false, null);
547 public string GetString (int len)
549 if (tdsVersion >= TdsVersion.tds70)
550 return GetString (len, true);
552 return GetString (len, false);
555 public string GetString (int len, bool wide, Encoding enc)
558 char[] chars = new char[len];
559 for (int i = 0; i < len; ++i) {
560 int lo = ((byte) GetByte ()) & 0xFF;
561 int hi = ((byte) GetByte ()) & 0xFF;
562 chars[i] = (char) (lo | ( hi << 8));
564 return new String (chars);
567 byte[] result = new byte[len];
568 Array.Copy (GetBytes (len, false), result, len);
569 // Use the passed encoder, if available
571 return (enc.GetString (result));
573 return (encoder.GetString (result));
577 public string GetString (int len, bool wide)
579 return GetString (len, wide, null);
582 public int GetNetShort ()
584 byte[] tmp = new byte[2];
587 return Ntohs (tmp, 0);
590 public short GetTdsShort ()
592 byte[] input = new byte[2];
594 for (int i = 0; i < 2; i += 1)
595 input[i] = GetByte ();
596 if(!BitConverter.IsLittleEndian)
597 return (BitConverter.ToInt16 (Swap (input), 0));
599 return (BitConverter.ToInt16 (input, 0));
603 public int GetTdsInt ()
605 byte[] input = new byte[4];
606 for (int i = 0; i < 4; i += 1) {
607 input[i] = GetByte ();
609 if(!BitConverter.IsLittleEndian)
610 return (BitConverter.ToInt32 (Swap (input), 0));
612 return (BitConverter.ToInt32 (input, 0));
615 public long GetTdsInt64 ()
617 byte[] input = new byte[8];
618 for (int i = 0; i < 8; i += 1)
619 input[i] = GetByte ();
620 if(!BitConverter.IsLittleEndian)
621 return (BitConverter.ToInt64 (Swap (input), 0));
623 return (BitConverter.ToInt64 (input, 0));
626 private void GetPhysicalPacket ()
628 int dataLength = GetPhysicalPacketHeader ();
629 GetPhysicalPacketData (dataLength);
632 int Read (byte [] buffer, int offset, int count)
635 return stream.Read (buffer, offset, count);
643 private int GetPhysicalPacketHeader ()
650 n = Read (tmpBuf, nread, 8 - nread);
654 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
659 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
660 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
662 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
665 // figure out how many bytes are remaining in this packet.
666 int len = Ntohs (tmpBuf, 2) - 8;
667 if (len >= inBuffer.Length)
668 inBuffer = new byte[len];
671 throw new Exception (String.Format ("Confused by a length of {0}", len));
678 private void GetPhysicalPacketData (int length)
684 while (nread < length) {
685 n = Read (inBuffer, nread, length - nread);
689 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
696 // adjust the bookkeeping info about the incoming buffer
697 inBufferLength = length;
702 private static int Ntohs (byte[] buf, int offset)
704 int lo = ((int) buf[offset + 1] & 0xff);
705 int hi = (((int) buf[offset] & 0xff ) << 8);
708 // return an int since we really want an _unsigned_
713 // If out of data, read another physical packet.
714 if (inBufferIndex >= inBufferLength)
715 GetPhysicalPacket ();
717 return inBuffer[inBufferIndex];
720 public bool Poll (int seconds, SelectMode selectMode)
722 return Poll (socket, seconds, selectMode);
725 private bool Poll (Socket s, int seconds, SelectMode selectMode)
727 long uSeconds = seconds * 1000000;
730 while (uSeconds > (long) Int32.MaxValue) {
731 bState = s.Poll (Int32.MaxValue, selectMode);
734 uSeconds -= Int32.MaxValue;
736 return s.Poll ((int) uSeconds, selectMode);
739 internal void ResizeOutBuf (int newSize)
741 if (newSize != outBufferLength) {
742 byte[] newBuf = new byte [newSize];
743 Buffer.BlockCopy (outBuffer, 0, newBuf, 0, newSize);
744 outBufferLength = newSize;
749 public bool ResetConnection {
750 get { return connReset; }
751 set { connReset = value; }
754 public void SendPacket ()
756 // Reset connection flag is only valid for SQLBatch/RPC/DTC messages
757 if (packetType != TdsPacketType.Query && packetType != TdsPacketType.RPC)
760 SendPhysicalPacket (true);
761 nextOutBufferIndex = 0;
762 packetType = TdsPacketType.None;
763 // Reset connection-reset flag to false - as any exception would anyway close
764 // the whole connection
769 private void SendPhysicalPacket (bool isLastSegment)
771 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
772 byte status = (byte) ((isLastSegment ? 0x01 : 0x00) | (connReset ? 0x08 : 0x00));
774 Store (0, (byte) packetType);
776 Store (2, (short) nextOutBufferIndex );
779 if (tdsVersion >= TdsVersion.tds70)
780 Store (6, (byte) packetsSent);
785 stream.Write (outBuffer, 0, nextOutBufferIndex);
791 public void Skip (long i)
797 public void StartPacket (TdsPacketType type)
799 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
800 inBufferIndex = inBufferLength;
803 nextOutBufferIndex = headerLength;
806 private void Store (int index, byte value)
808 outBuffer[index] = value;
811 private void Store (int index, short value)
813 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
814 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
817 #endregion // Methods
819 #region Async Methods
821 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
823 TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
825 stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
829 /// <returns>Packet size in bytes</returns>
830 public int EndReadPacket (IAsyncResult ar)
833 ar.AsyncWaitHandle.WaitOne ();
834 return (int) ((TdsAsyncResult) ar).ReturnValue;
838 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
840 TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
841 int nread = stream.EndRead (socketAsyncResult);
845 n = Read (tmpBuf, nread, 8 - nread);
849 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
854 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
855 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
857 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
860 // figure out how many bytes are remaining in this packet.
861 int len = Ntohs (tmpBuf, 2) - 8;
863 if (len >= inBuffer.Length)
864 inBuffer = new byte[len];
867 throw new Exception (String.Format ("Confused by a length of {0}", len));
870 GetPhysicalPacketData (len);
872 ar.ReturnValue = ((object)value); // packet size
876 #endregion // Async Methods