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 for (int i = 0; i < s.Length; i++) {
395 SendIfFull (sizeof(short));
396 AppendInternal ((short)s[i]);
401 // Appends with padding
402 public byte[] Append (string s, int len, byte pad)
407 byte[] result = encoder.GetBytes (s);
408 Append (result, len, pad);
412 public void Append (double value)
415 Append (Swap (BitConverter.GetBytes (value)), sizeof(double), (byte)0);
417 Append (BitConverter.GetBytes (value), sizeof(double), (byte)0);
420 public void Append (float value)
423 Append (Swap (BitConverter.GetBytes (value)), sizeof(float), (byte)0);
425 Append (BitConverter.GetBytes (value), sizeof(float), (byte)0);
428 public void Append (long l)
430 SendIfFull (sizeof (long));
432 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
433 AppendInternal ((int) ((int) (l & 0xffffffff)));
435 AppendInternal ((int) ((int) (l & 0xffffffff)));
436 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
440 public void Append (decimal d, int bytes)
442 int[] arr = Decimal.GetBits (d);
443 byte sign = (d > 0 ? (byte)1 : (byte)0);
446 AppendInternal (arr[0]);
447 AppendInternal (arr[1]);
448 AppendInternal (arr[2]);
449 AppendInternal ((int)0);
466 public bool IsConnected ()
468 return socket != null && socket.Connected && !(socket.Poll (0, SelectMode.SelectRead) && socket.Available == 0);
471 public byte GetByte ()
475 if (inBufferIndex >= inBufferLength) {
476 // out of data, read another physical packet.
477 GetPhysicalPacket ();
479 result = inBuffer[inBufferIndex++];
483 public byte[] GetBytes (int len, bool exclusiveBuffer)
485 byte[] result = null;
488 // Do not keep an internal result buffer larger than 16k.
489 // This would unnecessarily use up memory.
490 if (exclusiveBuffer || len > 16384)
491 result = new byte[len];
494 if (resBuffer.Length < len)
495 resBuffer = new byte[len];
501 if (inBufferIndex >= inBufferLength)
502 GetPhysicalPacket ();
504 int avail = inBufferLength - inBufferIndex;
505 avail = avail>len-i ? len-i : avail;
507 Buffer.BlockCopy (inBuffer, inBufferIndex, result, i, avail);
509 inBufferIndex += avail;
515 public string GetString (int len, Encoding enc)
517 if (tdsVersion >= TdsVersion.tds70)
518 return GetString (len, true, null);
520 return GetString (len, false, null);
523 public string GetString (int len)
525 if (tdsVersion >= TdsVersion.tds70)
526 return GetString (len, true);
528 return GetString (len, false);
531 public string GetString (int len, bool wide, Encoding enc)
534 char[] chars = new char[len];
535 for (int i = 0; i < len; ++i) {
536 int lo = ((byte) GetByte ()) & 0xFF;
537 int hi = ((byte) GetByte ()) & 0xFF;
538 chars[i] = (char) (lo | ( hi << 8));
540 return new String (chars);
543 byte[] result = new byte[len];
544 Array.Copy (GetBytes (len, false), result, len);
545 // Use the passed encoder, if available
547 return (enc.GetString (result));
549 return (encoder.GetString (result));
553 public string GetString (int len, bool wide)
555 return GetString (len, wide, null);
558 public int GetNetShort ()
560 byte[] tmp = new byte[2];
563 return Ntohs (tmp, 0);
566 public short GetTdsShort ()
568 byte[] input = new byte[2];
570 for (int i = 0; i < 2; i += 1)
571 input[i] = GetByte ();
572 if(!BitConverter.IsLittleEndian)
573 return (BitConverter.ToInt16 (Swap (input), 0));
575 return (BitConverter.ToInt16 (input, 0));
579 public int GetTdsInt ()
581 byte[] input = new byte[4];
582 for (int i = 0; i < 4; i += 1) {
583 input[i] = GetByte ();
585 if(!BitConverter.IsLittleEndian)
586 return (BitConverter.ToInt32 (Swap (input), 0));
588 return (BitConverter.ToInt32 (input, 0));
591 public long GetTdsInt64 ()
593 byte[] input = new byte[8];
594 for (int i = 0; i < 8; i += 1)
595 input[i] = GetByte ();
596 if(!BitConverter.IsLittleEndian)
597 return (BitConverter.ToInt64 (Swap (input), 0));
599 return (BitConverter.ToInt64 (input, 0));
602 private void GetPhysicalPacket ()
604 int dataLength = GetPhysicalPacketHeader ();
605 GetPhysicalPacketData (dataLength);
608 int Read (byte [] buffer, int offset, int count)
611 return stream.Read (buffer, offset, count);
619 private int GetPhysicalPacketHeader ()
626 n = Read (tmpBuf, nread, 8 - nread);
630 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
635 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
636 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
638 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
641 // figure out how many bytes are remaining in this packet.
642 int len = Ntohs (tmpBuf, 2) - 8;
643 if (len >= inBuffer.Length)
644 inBuffer = new byte[len];
647 throw new Exception (String.Format ("Confused by a length of {0}", len));
654 private void GetPhysicalPacketData (int length)
660 while (nread < length) {
661 n = Read (inBuffer, nread, length - nread);
665 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
672 // adjust the bookkeeping info about the incoming buffer
673 inBufferLength = length;
678 private static int Ntohs (byte[] buf, int offset)
680 int lo = ((int) buf[offset + 1] & 0xff);
681 int hi = (((int) buf[offset] & 0xff ) << 8);
684 // return an int since we really want an _unsigned_
689 // If out of data, read another physical packet.
690 if (inBufferIndex >= inBufferLength)
691 GetPhysicalPacket ();
693 return inBuffer[inBufferIndex];
696 public bool Poll (int seconds, SelectMode selectMode)
698 return Poll (socket, seconds, selectMode);
701 private bool Poll (Socket s, int seconds, SelectMode selectMode)
703 long uSeconds = seconds * 1000000;
706 while (uSeconds > (long) Int32.MaxValue) {
707 bState = s.Poll (Int32.MaxValue, selectMode);
710 uSeconds -= Int32.MaxValue;
712 return s.Poll ((int) uSeconds, selectMode);
715 internal void ResizeOutBuf (int newSize)
717 if (newSize != outBufferLength) {
718 byte[] newBuf = new byte [newSize];
719 Buffer.BlockCopy (outBuffer, 0, newBuf, 0, newSize);
720 outBufferLength = newSize;
725 public bool ResetConnection {
726 get { return connReset; }
727 set { connReset = value; }
730 public void SendPacket ()
732 // Reset connection flag is only valid for SQLBatch/RPC/DTC messages
733 if (packetType != TdsPacketType.Query && packetType != TdsPacketType.RPC)
736 SendPhysicalPacket (true);
737 nextOutBufferIndex = 0;
738 packetType = TdsPacketType.None;
739 // Reset connection-reset flag to false - as any exception would anyway close
740 // the whole connection
745 private void SendPhysicalPacket (bool isLastSegment)
747 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
748 byte status = (byte) ((isLastSegment ? 0x01 : 0x00) | (connReset ? 0x08 : 0x00));
750 Store (0, (byte) packetType);
752 Store (2, (short) nextOutBufferIndex );
755 if (tdsVersion >= TdsVersion.tds70)
756 Store (6, (byte) packetsSent);
761 stream.Write (outBuffer, 0, nextOutBufferIndex);
767 public void Skip (long i)
773 public void StartPacket (TdsPacketType type)
775 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
776 inBufferIndex = inBufferLength;
779 nextOutBufferIndex = headerLength;
782 private void Store (int index, byte value)
784 outBuffer[index] = value;
787 private void Store (int index, short value)
789 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
790 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
793 #endregion // Methods
795 #region Async Methods
797 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
799 TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
801 stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
805 /// <returns>Packet size in bytes</returns>
806 public int EndReadPacket (IAsyncResult ar)
809 ar.AsyncWaitHandle.WaitOne ();
810 return (int) ((TdsAsyncResult) ar).ReturnValue;
814 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
816 TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
817 int nread = stream.EndRead (socketAsyncResult);
821 n = Read (tmpBuf, nread, 8 - nread);
825 throw new IOException (n == 0 ? "Connection lost" : "Connection error");
830 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
831 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
833 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
836 // figure out how many bytes are remaining in this packet.
837 int len = Ntohs (tmpBuf, 2) - 8;
839 if (len >= inBuffer.Length)
840 inBuffer = new byte[len];
843 throw new Exception (String.Format ("Confused by a length of {0}", len));
846 GetPhysicalPacketData (len);
848 ar.ReturnValue = ((object)value); // packet size
852 #endregion // Async Methods